2008-11-20 23:01:55 +03: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-29 00:45:14 +04:00
|
|
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
2020-04-23 20:06:57 +03:00
|
|
|
* Copyright (c) 2011, 2020 by Delphix. All rights reserved.
|
2015-12-31 01:43:27 +03:00
|
|
|
* Copyright 2012 Milan Jurik. All rights reserved.
|
2012-05-10 02:05:14 +04:00
|
|
|
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
2013-05-25 06:06:23 +04:00
|
|
|
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
2017-01-18 01:45:02 +03:00
|
|
|
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
|
2017-02-04 00:24:44 +03:00
|
|
|
* Copyright 2016 Nexenta Systems, Inc.
|
2019-03-12 23:13:22 +03:00
|
|
|
* Copyright (c) 2019 Datto Inc.
|
2019-05-24 23:54:36 +03:00
|
|
|
* Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
|
2019-07-01 02:38:07 +03:00
|
|
|
* Copyright 2019 Joyent, Inc.
|
2019-11-11 10:24:14 +03:00
|
|
|
* Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
#include <sys/debug.h>
|
2008-11-20 23:01:55 +03:00
|
|
|
#include <errno.h>
|
2017-04-12 00:56:54 +03:00
|
|
|
#include <getopt.h>
|
2008-11-20 23:01:55 +03:00
|
|
|
#include <libgen.h>
|
|
|
|
#include <libintl.h>
|
|
|
|
#include <libuutil.h>
|
|
|
|
#include <libnvpair.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <zone.h>
|
2009-07-03 02:44:48 +04:00
|
|
|
#include <grp.h>
|
|
|
|
#include <pwd.h>
|
2010-08-27 01:24:34 +04:00
|
|
|
#include <signal.h>
|
2018-02-08 19:16:23 +03:00
|
|
|
#include <sys/debug.h>
|
2011-07-27 02:44:36 +04:00
|
|
|
#include <sys/list.h>
|
2008-11-20 23:01:55 +03:00
|
|
|
#include <sys/mkdev.h>
|
|
|
|
#include <sys/mntent.h>
|
|
|
|
#include <sys/mnttab.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/stat.h>
|
2009-07-03 02:44:48 +04:00
|
|
|
#include <sys/fs/zfs.h>
|
2017-08-10 01:31:08 +03:00
|
|
|
#include <sys/systeminfo.h>
|
2010-05-29 00:45:14 +04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <time.h>
|
2018-02-14 01:54:54 +03:00
|
|
|
#include <sys/zfs_project.h>
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
#include <libzfs.h>
|
2013-08-28 15:45:09 +04:00
|
|
|
#include <libzfs_core.h>
|
2011-07-27 02:44:36 +04:00
|
|
|
#include <zfs_prop.h>
|
|
|
|
#include <zfs_deleg.h>
|
2018-11-05 22:22:33 +03:00
|
|
|
#include <libzutil.h>
|
2008-11-20 23:01:55 +03:00
|
|
|
#include <libuutil.h>
|
2011-07-27 02:44:36 +04:00
|
|
|
#ifdef HAVE_IDMAP
|
|
|
|
#include <aclutils.h>
|
|
|
|
#include <directory.h>
|
|
|
|
#endif /* HAVE_IDMAP */
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
#include "zfs_iter.h"
|
|
|
|
#include "zfs_util.h"
|
2010-05-29 00:45:14 +04:00
|
|
|
#include "zfs_comutil.h"
|
2013-06-04 05:54:44 +04:00
|
|
|
#include "libzfs_impl.h"
|
2018-02-14 01:54:54 +03:00
|
|
|
#include "zfs_projectutil.h"
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
libzfs_handle_t *g_zfs;
|
|
|
|
|
|
|
|
static FILE *mnttab_file;
|
|
|
|
static char history_str[HIS_MAX_RECORD_LEN];
|
2013-08-28 15:45:09 +04:00
|
|
|
static boolean_t log_history = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
static int zfs_do_clone(int argc, char **argv);
|
|
|
|
static int zfs_do_create(int argc, char **argv);
|
|
|
|
static int zfs_do_destroy(int argc, char **argv);
|
|
|
|
static int zfs_do_get(int argc, char **argv);
|
|
|
|
static int zfs_do_inherit(int argc, char **argv);
|
|
|
|
static int zfs_do_list(int argc, char **argv);
|
|
|
|
static int zfs_do_mount(int argc, char **argv);
|
|
|
|
static int zfs_do_rename(int argc, char **argv);
|
|
|
|
static int zfs_do_rollback(int argc, char **argv);
|
|
|
|
static int zfs_do_set(int argc, char **argv);
|
|
|
|
static int zfs_do_upgrade(int argc, char **argv);
|
|
|
|
static int zfs_do_snapshot(int argc, char **argv);
|
|
|
|
static int zfs_do_unmount(int argc, char **argv);
|
|
|
|
static int zfs_do_share(int argc, char **argv);
|
|
|
|
static int zfs_do_unshare(int argc, char **argv);
|
|
|
|
static int zfs_do_send(int argc, char **argv);
|
|
|
|
static int zfs_do_receive(int argc, char **argv);
|
|
|
|
static int zfs_do_promote(int argc, char **argv);
|
2009-07-03 02:44:48 +04:00
|
|
|
static int zfs_do_userspace(int argc, char **argv);
|
2011-07-27 02:44:36 +04:00
|
|
|
static int zfs_do_allow(int argc, char **argv);
|
|
|
|
static int zfs_do_unallow(int argc, char **argv);
|
2009-08-18 22:43:27 +04:00
|
|
|
static int zfs_do_hold(int argc, char **argv);
|
2011-07-27 02:44:36 +04:00
|
|
|
static int zfs_do_holds(int argc, char **argv);
|
2009-08-18 22:43:27 +04:00
|
|
|
static int zfs_do_release(int argc, char **argv);
|
2010-08-27 01:24:34 +04:00
|
|
|
static int zfs_do_diff(int argc, char **argv);
|
2013-12-12 02:33:41 +04:00
|
|
|
static int zfs_do_bookmark(int argc, char **argv);
|
2018-02-08 19:16:23 +03:00
|
|
|
static int zfs_do_channel_program(int argc, char **argv);
|
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 20:36:48 +03:00
|
|
|
static int zfs_do_load_key(int argc, char **argv);
|
|
|
|
static int zfs_do_unload_key(int argc, char **argv);
|
|
|
|
static int zfs_do_change_key(int argc, char **argv);
|
2018-02-14 01:54:54 +03:00
|
|
|
static int zfs_do_project(int argc, char **argv);
|
2019-04-10 10:43:28 +03:00
|
|
|
static int zfs_do_version(int argc, char **argv);
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
static int zfs_do_redact(int argc, char **argv);
|
2020-04-01 20:02:06 +03:00
|
|
|
static int zfs_do_wait(int argc, char **argv);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2019-12-11 22:58:37 +03:00
|
|
|
#ifdef __FreeBSD__
|
|
|
|
static int zfs_do_jail(int argc, char **argv);
|
|
|
|
static int zfs_do_unjail(int argc, char **argv);
|
|
|
|
#endif
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
2008-12-03 23:09:06 +03:00
|
|
|
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
2008-12-03 23:09:06 +03:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2008-11-20 23:01:55 +03:00
|
|
|
const char *
|
|
|
|
_umem_debug_init(void)
|
|
|
|
{
|
|
|
|
return ("default,verbose"); /* $UMEM_DEBUG setting */
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
_umem_logging_init(void)
|
|
|
|
{
|
|
|
|
return ("fail,contents"); /* $UMEM_LOGGING setting */
|
|
|
|
}
|
2008-12-03 23:09:06 +03:00
|
|
|
#endif
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
HELP_CLONE,
|
|
|
|
HELP_CREATE,
|
|
|
|
HELP_DESTROY,
|
|
|
|
HELP_GET,
|
|
|
|
HELP_INHERIT,
|
|
|
|
HELP_UPGRADE,
|
|
|
|
HELP_LIST,
|
|
|
|
HELP_MOUNT,
|
|
|
|
HELP_PROMOTE,
|
|
|
|
HELP_RECEIVE,
|
|
|
|
HELP_RENAME,
|
|
|
|
HELP_ROLLBACK,
|
|
|
|
HELP_SEND,
|
|
|
|
HELP_SET,
|
|
|
|
HELP_SHARE,
|
|
|
|
HELP_SNAPSHOT,
|
|
|
|
HELP_UNMOUNT,
|
|
|
|
HELP_UNSHARE,
|
|
|
|
HELP_ALLOW,
|
2009-07-03 02:44:48 +04:00
|
|
|
HELP_UNALLOW,
|
|
|
|
HELP_USERSPACE,
|
2009-08-18 22:43:27 +04:00
|
|
|
HELP_GROUPSPACE,
|
2018-02-14 01:54:54 +03:00
|
|
|
HELP_PROJECTSPACE,
|
|
|
|
HELP_PROJECT,
|
2009-08-18 22:43:27 +04:00
|
|
|
HELP_HOLD,
|
|
|
|
HELP_HOLDS,
|
2010-08-27 01:24:34 +04:00
|
|
|
HELP_RELEASE,
|
2011-11-17 22:14:36 +04:00
|
|
|
HELP_DIFF,
|
2013-12-12 02:33:41 +04:00
|
|
|
HELP_BOOKMARK,
|
2018-02-08 19:16:23 +03:00
|
|
|
HELP_CHANNEL_PROGRAM,
|
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 20:36:48 +03:00
|
|
|
HELP_LOAD_KEY,
|
|
|
|
HELP_UNLOAD_KEY,
|
|
|
|
HELP_CHANGE_KEY,
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
HELP_VERSION,
|
|
|
|
HELP_REDACT,
|
2019-12-11 22:58:37 +03:00
|
|
|
HELP_JAIL,
|
2020-04-01 20:02:06 +03:00
|
|
|
HELP_UNJAIL,
|
|
|
|
HELP_WAIT,
|
2008-11-20 23:01:55 +03:00
|
|
|
} zfs_help_t;
|
|
|
|
|
|
|
|
typedef struct zfs_command {
|
|
|
|
const char *name;
|
|
|
|
int (*func)(int argc, char **argv);
|
|
|
|
zfs_help_t usage;
|
|
|
|
} zfs_command_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Master command table. Each ZFS command has a name, associated function, and
|
|
|
|
* usage message. The usage messages need to be internationalized, so we have
|
|
|
|
* to have a function to return the usage message based on a command index.
|
|
|
|
*
|
|
|
|
* These commands are organized according to how they are displayed in the usage
|
|
|
|
* message. An empty command (one with a NULL name) indicates an empty line in
|
|
|
|
* the generic usage message.
|
|
|
|
*/
|
|
|
|
static zfs_command_t command_table[] = {
|
2019-04-10 10:43:28 +03:00
|
|
|
{ "version", zfs_do_version, HELP_VERSION },
|
|
|
|
{ NULL },
|
2008-11-20 23:01:55 +03:00
|
|
|
{ "create", zfs_do_create, HELP_CREATE },
|
|
|
|
{ "destroy", zfs_do_destroy, HELP_DESTROY },
|
|
|
|
{ NULL },
|
|
|
|
{ "snapshot", zfs_do_snapshot, HELP_SNAPSHOT },
|
|
|
|
{ "rollback", zfs_do_rollback, HELP_ROLLBACK },
|
|
|
|
{ "clone", zfs_do_clone, HELP_CLONE },
|
|
|
|
{ "promote", zfs_do_promote, HELP_PROMOTE },
|
|
|
|
{ "rename", zfs_do_rename, HELP_RENAME },
|
2013-12-12 02:33:41 +04:00
|
|
|
{ "bookmark", zfs_do_bookmark, HELP_BOOKMARK },
|
2018-02-08 19:16:23 +03:00
|
|
|
{ "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM },
|
2008-11-20 23:01:55 +03:00
|
|
|
{ NULL },
|
|
|
|
{ "list", zfs_do_list, HELP_LIST },
|
|
|
|
{ NULL },
|
|
|
|
{ "set", zfs_do_set, HELP_SET },
|
2010-05-29 00:45:14 +04:00
|
|
|
{ "get", zfs_do_get, HELP_GET },
|
2008-11-20 23:01:55 +03:00
|
|
|
{ "inherit", zfs_do_inherit, HELP_INHERIT },
|
|
|
|
{ "upgrade", zfs_do_upgrade, HELP_UPGRADE },
|
2018-02-14 01:54:54 +03:00
|
|
|
{ NULL },
|
2009-07-03 02:44:48 +04:00
|
|
|
{ "userspace", zfs_do_userspace, HELP_USERSPACE },
|
|
|
|
{ "groupspace", zfs_do_userspace, HELP_GROUPSPACE },
|
2018-02-14 01:54:54 +03:00
|
|
|
{ "projectspace", zfs_do_userspace, HELP_PROJECTSPACE },
|
|
|
|
{ NULL },
|
|
|
|
{ "project", zfs_do_project, HELP_PROJECT },
|
2008-11-20 23:01:55 +03:00
|
|
|
{ NULL },
|
|
|
|
{ "mount", zfs_do_mount, HELP_MOUNT },
|
|
|
|
{ "unmount", zfs_do_unmount, HELP_UNMOUNT },
|
|
|
|
{ "share", zfs_do_share, HELP_SHARE },
|
|
|
|
{ "unshare", zfs_do_unshare, HELP_UNSHARE },
|
|
|
|
{ NULL },
|
|
|
|
{ "send", zfs_do_send, HELP_SEND },
|
|
|
|
{ "receive", zfs_do_receive, HELP_RECEIVE },
|
|
|
|
{ NULL },
|
2011-07-27 02:44:36 +04:00
|
|
|
{ "allow", zfs_do_allow, HELP_ALLOW },
|
2008-11-20 23:01:55 +03:00
|
|
|
{ NULL },
|
2011-07-27 02:44:36 +04:00
|
|
|
{ "unallow", zfs_do_unallow, HELP_UNALLOW },
|
2009-08-18 22:43:27 +04:00
|
|
|
{ NULL },
|
|
|
|
{ "hold", zfs_do_hold, HELP_HOLD },
|
2011-07-27 02:44:36 +04:00
|
|
|
{ "holds", zfs_do_holds, HELP_HOLDS },
|
2009-08-18 22:43:27 +04:00
|
|
|
{ "release", zfs_do_release, HELP_RELEASE },
|
2010-08-27 01:24:34 +04:00
|
|
|
{ "diff", zfs_do_diff, HELP_DIFF },
|
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 20:36:48 +03:00
|
|
|
{ "load-key", zfs_do_load_key, HELP_LOAD_KEY },
|
|
|
|
{ "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY },
|
|
|
|
{ "change-key", zfs_do_change_key, HELP_CHANGE_KEY },
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
{ "redact", zfs_do_redact, HELP_REDACT },
|
2020-04-01 20:02:06 +03:00
|
|
|
{ "wait", zfs_do_wait, HELP_WAIT },
|
2019-12-11 22:58:37 +03:00
|
|
|
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
{ "jail", zfs_do_jail, HELP_JAIL },
|
|
|
|
{ "unjail", zfs_do_unjail, HELP_UNJAIL },
|
|
|
|
#endif
|
2008-11-20 23:01:55 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
|
|
|
|
|
|
|
|
zfs_command_t *current_command;
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
get_usage(zfs_help_t idx)
|
|
|
|
{
|
|
|
|
switch (idx) {
|
|
|
|
case HELP_CLONE:
|
2008-12-03 23:09:06 +03:00
|
|
|
return (gettext("\tclone [-p] [-o property=value] ... "
|
|
|
|
"<snapshot> <filesystem|volume>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_CREATE:
|
2019-07-16 21:19:24 +03:00
|
|
|
return (gettext("\tcreate [-Pnpv] [-o property=value] ... "
|
2008-11-20 23:01:55 +03:00
|
|
|
"<filesystem>\n"
|
2019-07-16 21:19:24 +03:00
|
|
|
"\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
|
2008-11-20 23:01:55 +03:00
|
|
|
"-V <size> <volume>\n"));
|
|
|
|
case HELP_DESTROY:
|
2011-11-17 22:14:36 +04:00
|
|
|
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
|
|
|
|
"\tdestroy [-dnpRrv] "
|
2013-12-12 02:33:41 +04:00
|
|
|
"<filesystem|volume>@<snap>[%<snap>][,...]\n"
|
|
|
|
"\tdestroy <filesystem|volume>#<bookmark>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_GET:
|
2009-07-03 02:44:48 +04:00
|
|
|
return (gettext("\tget [-rHp] [-d max] "
|
2013-12-12 02:33:41 +04:00
|
|
|
"[-o \"all\" | field[,...]]\n"
|
|
|
|
"\t [-t type[,...]] [-s source[,...]]\n"
|
2008-11-20 23:01:55 +03:00
|
|
|
"\t <\"all\" | property[,...]> "
|
2017-01-27 01:42:15 +03:00
|
|
|
"[filesystem|volume|snapshot|bookmark] ...\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_INHERIT:
|
2010-05-29 00:45:14 +04:00
|
|
|
return (gettext("\tinherit [-rS] <property> "
|
2008-12-03 23:09:06 +03:00
|
|
|
"<filesystem|volume|snapshot> ...\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_UPGRADE:
|
|
|
|
return (gettext("\tupgrade [-v]\n"
|
|
|
|
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
|
|
|
|
case HELP_LIST:
|
2013-11-19 19:41:37 +04:00
|
|
|
return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
|
|
|
|
"[-s property]...\n\t [-S property]... [-t type[,...]] "
|
2013-10-24 03:45:45 +04:00
|
|
|
"[filesystem|volume|snapshot] ...\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_MOUNT:
|
|
|
|
return (gettext("\tmount\n"
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
"\tmount [-flvO] [-o opts] <-a | filesystem>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_PROMOTE:
|
|
|
|
return (gettext("\tpromote <clone-filesystem>\n"));
|
|
|
|
case HELP_RECEIVE:
|
2020-03-17 20:08:32 +03:00
|
|
|
return (gettext("\treceive [-vMnsFhu] "
|
2017-05-10 02:21:09 +03:00
|
|
|
"[-o <property>=<value>] ... [-x <property>] ...\n"
|
|
|
|
"\t <filesystem|volume|snapshot>\n"
|
2020-03-17 20:08:32 +03:00
|
|
|
"\treceive [-vMnsFhu] [-o <property>=<value>] ... "
|
2017-05-10 02:21:09 +03:00
|
|
|
"[-x <property>] ... \n"
|
|
|
|
"\t [-d | -e] <filesystem>\n"
|
2016-01-07 00:22:48 +03:00
|
|
|
"\treceive -A <filesystem|volume>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_RENAME:
|
2012-04-27 22:14:46 +04:00
|
|
|
return (gettext("\trename [-f] <filesystem|volume|snapshot> "
|
2008-11-20 23:01:55 +03:00
|
|
|
"<filesystem|volume|snapshot>\n"
|
2012-04-27 22:14:46 +04:00
|
|
|
"\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
|
2013-12-12 02:33:41 +04:00
|
|
|
"\trename -r <snapshot> <snapshot>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_ROLLBACK:
|
|
|
|
return (gettext("\trollback [-rRf] <snapshot>\n"));
|
|
|
|
case HELP_SEND:
|
2019-02-15 23:41:38 +03:00
|
|
|
return (gettext("\tsend [-DnPpRvLecwhb] [-[i|I] snapshot] "
|
2013-12-12 02:33:41 +04:00
|
|
|
"<snapshot>\n"
|
2018-02-21 23:32:06 +03:00
|
|
|
"\tsend [-nvPLecw] [-i snapshot|bookmark] "
|
2016-01-07 00:22:48 +03:00
|
|
|
"<filesystem|volume|snapshot>\n"
|
2019-06-21 19:38:15 +03:00
|
|
|
"\tsend [-DnPpvLec] [-i bookmark|snapshot] "
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
"--redact <bookmark> <snapshot>\n"
|
2020-01-10 21:16:58 +03:00
|
|
|
"\tsend [-nvPe] -t <receive_resume_token>\n"
|
|
|
|
"\tsend [-Pnv] --saved filesystem\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_SET:
|
2015-07-06 02:11:09 +03:00
|
|
|
return (gettext("\tset <property=value> ... "
|
2008-12-03 23:09:06 +03:00
|
|
|
"<filesystem|volume|snapshot> ...\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_SHARE:
|
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 20:36:48 +03:00
|
|
|
return (gettext("\tshare [-l] <-a [nfs|smb] | filesystem>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_SNAPSHOT:
|
2018-01-30 02:05:03 +03:00
|
|
|
return (gettext("\tsnapshot [-r] [-o property=value] ... "
|
2013-12-12 02:33:41 +04:00
|
|
|
"<filesystem|volume>@<snap> ...\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_UNMOUNT:
|
2019-06-28 22:38:37 +03:00
|
|
|
return (gettext("\tunmount [-fu] "
|
2008-11-20 23:01:55 +03:00
|
|
|
"<-a | filesystem|mountpoint>\n"));
|
|
|
|
case HELP_UNSHARE:
|
2009-08-18 22:43:27 +04:00
|
|
|
return (gettext("\tunshare "
|
2016-11-29 22:22:38 +03:00
|
|
|
"<-a [nfs|smb] | filesystem|mountpoint>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
case HELP_ALLOW:
|
2009-07-03 02:44:48 +04:00
|
|
|
return (gettext("\tallow <filesystem|volume>\n"
|
|
|
|
"\tallow [-ldug] "
|
2008-11-20 23:01:55 +03:00
|
|
|
"<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
|
|
|
|
"\t <filesystem|volume>\n"
|
|
|
|
"\tallow [-ld] -e <perm|@setname>[,...] "
|
|
|
|
"<filesystem|volume>\n"
|
|
|
|
"\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
|
|
|
|
"\tallow -s @setname <perm|@setname>[,...] "
|
|
|
|
"<filesystem|volume>\n"));
|
|
|
|
case HELP_UNALLOW:
|
|
|
|
return (gettext("\tunallow [-rldug] "
|
|
|
|
"<\"everyone\"|user|group>[,...]\n"
|
|
|
|
"\t [<perm|@setname>[,...]] <filesystem|volume>\n"
|
|
|
|
"\tunallow [-rld] -e [<perm|@setname>[,...]] "
|
|
|
|
"<filesystem|volume>\n"
|
|
|
|
"\tunallow [-r] -c [<perm|@setname>[,...]] "
|
|
|
|
"<filesystem|volume>\n"
|
|
|
|
"\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
|
|
|
|
"<filesystem|volume>\n"));
|
2009-07-03 02:44:48 +04:00
|
|
|
case HELP_USERSPACE:
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
|
2013-12-12 02:33:41 +04:00
|
|
|
"[-s field] ...\n"
|
|
|
|
"\t [-S field] ... [-t type[,...]] "
|
2013-11-19 19:41:37 +04:00
|
|
|
"<filesystem|snapshot>\n"));
|
2009-07-03 02:44:48 +04:00
|
|
|
case HELP_GROUPSPACE:
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
|
2013-12-12 02:33:41 +04:00
|
|
|
"[-s field] ...\n"
|
|
|
|
"\t [-S field] ... [-t type[,...]] "
|
2013-11-19 19:41:37 +04:00
|
|
|
"<filesystem|snapshot>\n"));
|
2018-02-14 01:54:54 +03:00
|
|
|
case HELP_PROJECTSPACE:
|
|
|
|
return (gettext("\tprojectspace [-Hp] [-o field[,...]] "
|
|
|
|
"[-s field] ... \n"
|
|
|
|
"\t [-S field] ... <filesystem|snapshot>\n"));
|
|
|
|
case HELP_PROJECT:
|
|
|
|
return (gettext("\tproject [-d|-r] <directory|file ...>\n"
|
|
|
|
"\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n"
|
|
|
|
"\tproject -C [-k] [-r] <directory ...>\n"
|
|
|
|
"\tproject [-p id] [-r] [-s] <directory ...>\n"));
|
2009-08-18 22:43:27 +04:00
|
|
|
case HELP_HOLD:
|
|
|
|
return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
|
|
|
|
case HELP_HOLDS:
|
2018-08-19 01:47:41 +03:00
|
|
|
return (gettext("\tholds [-rH] <snapshot> ...\n"));
|
2009-08-18 22:43:27 +04:00
|
|
|
case HELP_RELEASE:
|
|
|
|
return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
|
2010-08-27 01:24:34 +04:00
|
|
|
case HELP_DIFF:
|
|
|
|
return (gettext("\tdiff [-FHt] <snapshot> "
|
|
|
|
"[snapshot|filesystem]\n"));
|
2013-12-12 02:33:41 +04:00
|
|
|
case HELP_BOOKMARK:
|
2019-11-11 10:24:14 +03:00
|
|
|
return (gettext("\tbookmark <snapshot|bookmark> "
|
|
|
|
"<newbookmark>\n"));
|
2018-02-08 19:16:23 +03:00
|
|
|
case HELP_CHANNEL_PROGRAM:
|
2018-03-19 22:40:58 +03:00
|
|
|
return (gettext("\tprogram [-jn] [-t <instruction limit>] "
|
2019-02-26 22:15:28 +03:00
|
|
|
"[-m <memory limit (b)>]\n"
|
|
|
|
"\t <pool> <program file> [lua args...]\n"));
|
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 20:36:48 +03:00
|
|
|
case HELP_LOAD_KEY:
|
|
|
|
return (gettext("\tload-key [-rn] [-L <keylocation>] "
|
|
|
|
"<-a | filesystem|volume>\n"));
|
|
|
|
case HELP_UNLOAD_KEY:
|
|
|
|
return (gettext("\tunload-key [-r] "
|
|
|
|
"<-a | filesystem|volume>\n"));
|
|
|
|
case HELP_CHANGE_KEY:
|
2017-11-08 22:12:59 +03:00
|
|
|
return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"
|
|
|
|
"\t [-o keylocation=<value>] [-o pbkfd2iters=<value>]\n"
|
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 20:36:48 +03:00
|
|
|
"\t <filesystem|volume>\n"
|
|
|
|
"\tchange-key -i [-l] <filesystem|volume>\n"));
|
2019-04-10 10:43:28 +03:00
|
|
|
case HELP_VERSION:
|
|
|
|
return (gettext("\tversion\n"));
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
case HELP_REDACT:
|
|
|
|
return (gettext("\tredact <snapshot> <bookmark> "
|
2020-02-26 03:20:50 +03:00
|
|
|
"<redaction_snapshot> ...\n"));
|
2019-12-11 22:58:37 +03:00
|
|
|
case HELP_JAIL:
|
|
|
|
return (gettext("\tjail <jailid|jailname> <filesystem>\n"));
|
|
|
|
case HELP_UNJAIL:
|
|
|
|
return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));
|
2020-04-01 20:02:06 +03:00
|
|
|
case HELP_WAIT:
|
|
|
|
return (gettext("\twait [-t <activity>] <filesystem>\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
abort();
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
void
|
|
|
|
nomem(void)
|
|
|
|
{
|
|
|
|
(void) fprintf(stderr, gettext("internal error: out of memory\n"));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
|
|
|
* Utility function to guarantee malloc() success.
|
|
|
|
*/
|
2010-05-29 00:45:14 +04:00
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
void *
|
|
|
|
safe_malloc(size_t size)
|
|
|
|
{
|
|
|
|
void *data;
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if ((data = calloc(1, size)) == NULL)
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
return (data);
|
|
|
|
}
|
|
|
|
|
2018-02-08 19:16:23 +03:00
|
|
|
void *
|
|
|
|
safe_realloc(void *data, size_t size)
|
|
|
|
{
|
|
|
|
void *newp;
|
|
|
|
if ((newp = realloc(data, size)) == NULL) {
|
|
|
|
free(data);
|
|
|
|
nomem();
|
|
|
|
}
|
|
|
|
|
|
|
|
return (newp);
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
static char *
|
|
|
|
safe_strdup(char *str)
|
|
|
|
{
|
|
|
|
char *dupstr = strdup(str);
|
|
|
|
|
|
|
|
if (dupstr == NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
return (dupstr);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
|
|
|
* Callback routine that will print out information for each of
|
|
|
|
* the properties.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
usage_prop_cb(int prop, void *cb)
|
|
|
|
{
|
|
|
|
FILE *fp = cb;
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
(void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
if (zfs_prop_readonly(prop))
|
|
|
|
(void) fprintf(fp, " NO ");
|
2008-11-20 23:01:55 +03:00
|
|
|
else
|
2008-12-03 23:09:06 +03:00
|
|
|
(void) fprintf(fp, "YES ");
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (zfs_prop_inheritable(prop))
|
|
|
|
(void) fprintf(fp, " YES ");
|
|
|
|
else
|
|
|
|
(void) fprintf(fp, " NO ");
|
|
|
|
|
|
|
|
if (zfs_prop_values(prop) == NULL)
|
|
|
|
(void) fprintf(fp, "-\n");
|
|
|
|
else
|
|
|
|
(void) fprintf(fp, "%s\n", zfs_prop_values(prop));
|
|
|
|
|
|
|
|
return (ZPROP_CONT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display usage message. If we're inside a command, display only the usage for
|
|
|
|
* that command. Otherwise, iterate over the entire command table and display
|
|
|
|
* a complete usage message.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
usage(boolean_t requested)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
boolean_t show_properties = B_FALSE;
|
|
|
|
FILE *fp = requested ? stdout : stderr;
|
|
|
|
|
|
|
|
if (current_command == NULL) {
|
|
|
|
|
|
|
|
(void) fprintf(fp, gettext("usage: zfs command args ...\n"));
|
|
|
|
(void) fprintf(fp,
|
|
|
|
gettext("where 'command' is one of the following:\n\n"));
|
|
|
|
|
|
|
|
for (i = 0; i < NCOMMAND; i++) {
|
|
|
|
if (command_table[i].name == NULL)
|
|
|
|
(void) fprintf(fp, "\n");
|
|
|
|
else
|
|
|
|
(void) fprintf(fp, "%s",
|
|
|
|
get_usage(command_table[i].usage));
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) fprintf(fp, gettext("\nEach dataset is of the form: "
|
|
|
|
"pool/[dataset/]*dataset[@name]\n"));
|
|
|
|
} else {
|
|
|
|
(void) fprintf(fp, gettext("usage:\n"));
|
|
|
|
(void) fprintf(fp, "%s", get_usage(current_command->usage));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current_command != NULL &&
|
|
|
|
(strcmp(current_command->name, "set") == 0 ||
|
|
|
|
strcmp(current_command->name, "get") == 0 ||
|
|
|
|
strcmp(current_command->name, "inherit") == 0 ||
|
|
|
|
strcmp(current_command->name, "list") == 0))
|
|
|
|
show_properties = B_TRUE;
|
|
|
|
|
|
|
|
if (show_properties) {
|
|
|
|
(void) fprintf(fp,
|
|
|
|
gettext("\nThe following properties are supported:\n"));
|
|
|
|
|
|
|
|
(void) fprintf(fp, "\n\t%-14s %s %s %s\n\n",
|
|
|
|
"PROPERTY", "EDIT", "INHERIT", "VALUES");
|
|
|
|
|
|
|
|
/* Iterate over all properties */
|
|
|
|
(void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
|
|
|
|
ZFS_TYPE_DATASET);
|
|
|
|
|
2009-07-03 02:44:48 +04:00
|
|
|
(void) fprintf(fp, "\t%-15s ", "userused@...");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "groupused@...");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
2018-02-14 01:54:54 +03:00
|
|
|
(void) fprintf(fp, "\t%-15s ", "projectused@...");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "userobjused@...");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "groupobjused@...");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "projectobjused@...");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
2009-07-03 02:44:48 +04:00
|
|
|
(void) fprintf(fp, "\t%-15s ", "userquota@...");
|
|
|
|
(void) fprintf(fp, "YES NO <size> | none\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "groupquota@...");
|
|
|
|
(void) fprintf(fp, "YES NO <size> | none\n");
|
2018-02-14 01:54:54 +03:00
|
|
|
(void) fprintf(fp, "\t%-15s ", "projectquota@...");
|
|
|
|
(void) fprintf(fp, "YES NO <size> | none\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "userobjquota@...");
|
|
|
|
(void) fprintf(fp, "YES NO <size> | none\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "groupobjquota@...");
|
|
|
|
(void) fprintf(fp, "YES NO <size> | none\n");
|
|
|
|
(void) fprintf(fp, "\t%-15s ", "projectobjquota@...");
|
|
|
|
(void) fprintf(fp, "YES NO <size> | none\n");
|
2011-11-17 22:14:36 +04:00
|
|
|
(void) fprintf(fp, "\t%-15s ", "written@<snap>");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
(void) fprintf(fp, "\t%-15s ", "written#<bookmark>");
|
|
|
|
(void) fprintf(fp, " NO NO <size>\n");
|
2009-07-03 02:44:48 +04:00
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(fp, gettext("\nSizes are specified in bytes "
|
|
|
|
"with standard units such as K, M, G, etc.\n"));
|
2008-12-03 23:09:06 +03:00
|
|
|
(void) fprintf(fp, gettext("\nUser-defined properties can "
|
2008-11-20 23:01:55 +03:00
|
|
|
"be specified by using a name containing a colon (:).\n"));
|
2018-02-14 01:54:54 +03:00
|
|
|
(void) fprintf(fp, gettext("\nThe {user|group|project}"
|
|
|
|
"[obj]{used|quota}@ properties must be appended with\n"
|
|
|
|
"a user|group|project specifier of one of these forms:\n"
|
2009-07-03 02:44:48 +04:00
|
|
|
" POSIX name (eg: \"matt\")\n"
|
|
|
|
" POSIX id (eg: \"126829\")\n"
|
|
|
|
" SMB name@domain (eg: \"matt@sun\")\n"
|
|
|
|
" SMB SID (eg: \"S-1-234-567-89\")\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
} else {
|
|
|
|
(void) fprintf(fp,
|
2009-01-16 00:59:39 +03:00
|
|
|
gettext("\nFor the property list, run: %s\n"),
|
|
|
|
"zfs set|get");
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(fp,
|
2009-01-16 00:59:39 +03:00
|
|
|
gettext("\nFor the delegated permission list, run: %s\n"),
|
|
|
|
"zfs allow|unallow");
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See comments at end of main().
|
|
|
|
*/
|
|
|
|
if (getenv("ZFS_ABORT") != NULL) {
|
|
|
|
(void) printf("dumping core by request\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
exit(requested ? 0 : 2);
|
|
|
|
}
|
|
|
|
|
2015-07-06 02:11:09 +03:00
|
|
|
/*
|
|
|
|
* Take a property=value argument string and add it to the given nvlist.
|
|
|
|
* Modifies the argument inplace.
|
|
|
|
*/
|
2017-05-10 02:21:09 +03:00
|
|
|
static boolean_t
|
2015-07-06 02:11:09 +03:00
|
|
|
parseprop(nvlist_t *props, char *propname)
|
2008-12-03 23:09:06 +03:00
|
|
|
{
|
2017-05-10 02:21:09 +03:00
|
|
|
char *propval;
|
2008-12-03 23:09:06 +03:00
|
|
|
|
|
|
|
if ((propval = strchr(propname, '=')) == NULL) {
|
|
|
|
(void) fprintf(stderr, gettext("missing "
|
2015-07-06 02:11:09 +03:00
|
|
|
"'=' for property=value argument\n"));
|
2017-05-10 02:21:09 +03:00
|
|
|
return (B_FALSE);
|
2008-12-03 23:09:06 +03:00
|
|
|
}
|
|
|
|
*propval = '\0';
|
|
|
|
propval++;
|
2017-05-10 02:21:09 +03:00
|
|
|
if (nvlist_exists(props, propname)) {
|
2008-12-03 23:09:06 +03:00
|
|
|
(void) fprintf(stderr, gettext("property '%s' "
|
|
|
|
"specified multiple times\n"), propname);
|
2017-05-10 02:21:09 +03:00
|
|
|
return (B_FALSE);
|
2008-12-03 23:09:06 +03:00
|
|
|
}
|
2010-05-29 00:45:14 +04:00
|
|
|
if (nvlist_add_string(props, propname, propval) != 0)
|
|
|
|
nomem();
|
2017-05-10 02:21:09 +03:00
|
|
|
return (B_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Take a property name argument and add it to the given nvlist.
|
|
|
|
* Modifies the argument inplace.
|
|
|
|
*/
|
|
|
|
static boolean_t
|
|
|
|
parsepropname(nvlist_t *props, char *propname)
|
|
|
|
{
|
|
|
|
if (strchr(propname, '=') != NULL) {
|
|
|
|
(void) fprintf(stderr, gettext("invalid character "
|
|
|
|
"'=' in property argument\n"));
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
if (nvlist_exists(props, propname)) {
|
|
|
|
(void) fprintf(stderr, gettext("property '%s' "
|
|
|
|
"specified multiple times\n"), propname);
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
if (nvlist_add_boolean(props, propname) != 0)
|
|
|
|
nomem();
|
|
|
|
return (B_TRUE);
|
2008-12-03 23:09:06 +03:00
|
|
|
}
|
|
|
|
|
2009-07-03 02:44:48 +04:00
|
|
|
static int
|
|
|
|
parse_depth(char *opt, int *flags)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
int depth;
|
|
|
|
|
|
|
|
depth = (int)strtol(opt, &tmp, 0);
|
|
|
|
if (*tmp) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("%s is not an integer\n"), optarg);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (depth < 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Depth can not be negative.\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
*flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
|
|
|
|
return (depth);
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
#define PROGRESS_DELAY 2 /* seconds */
|
|
|
|
|
|
|
|
static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
|
|
|
|
static time_t pt_begin;
|
|
|
|
static char *pt_header = NULL;
|
|
|
|
static boolean_t pt_shown;
|
|
|
|
|
|
|
|
static void
|
|
|
|
start_progress_timer(void)
|
|
|
|
{
|
|
|
|
pt_begin = time(NULL) + PROGRESS_DELAY;
|
|
|
|
pt_shown = B_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_progress_header(char *header)
|
|
|
|
{
|
|
|
|
assert(pt_header == NULL);
|
|
|
|
pt_header = safe_strdup(header);
|
|
|
|
if (pt_shown) {
|
|
|
|
(void) printf("%s: ", header);
|
|
|
|
(void) fflush(stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_progress(char *update)
|
|
|
|
{
|
|
|
|
if (!pt_shown && time(NULL) > pt_begin) {
|
|
|
|
int len = strlen(update);
|
|
|
|
|
|
|
|
(void) printf("%s: %s%*.*s", pt_header, update, len, len,
|
|
|
|
pt_reverse);
|
|
|
|
(void) fflush(stdout);
|
|
|
|
pt_shown = B_TRUE;
|
|
|
|
} else if (pt_shown) {
|
|
|
|
int len = strlen(update);
|
|
|
|
|
|
|
|
(void) printf("%s%*.*s", update, len, len, pt_reverse);
|
|
|
|
(void) fflush(stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
finish_progress(char *done)
|
|
|
|
{
|
|
|
|
if (pt_shown) {
|
|
|
|
(void) printf("%s\n", done);
|
|
|
|
(void) fflush(stdout);
|
|
|
|
}
|
|
|
|
free(pt_header);
|
|
|
|
pt_header = NULL;
|
|
|
|
}
|
2010-08-26 22:56:53 +04:00
|
|
|
|
2015-05-29 19:27:03 +03:00
|
|
|
static int
|
|
|
|
zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
|
|
|
|
{
|
|
|
|
zfs_handle_t *zhp = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
zhp = zfs_open(hdl, dataset, type);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Volumes may neither be mounted or shared. Potentially in the
|
|
|
|
* future filesystems detected on these volumes could be mounted.
|
|
|
|
*/
|
|
|
|
if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mount and/or share the new filesystem as appropriate. We provide a
|
|
|
|
* verbose error message to let the user know that their filesystem was
|
|
|
|
* in fact created, even if we failed to mount or share it.
|
|
|
|
*
|
|
|
|
* If the user doesn't want the dataset automatically mounted, then
|
|
|
|
* skip the mount/share step
|
|
|
|
*/
|
|
|
|
if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) &&
|
|
|
|
zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) {
|
2019-12-11 22:58:37 +03:00
|
|
|
if (zfs_mount_delegation_check()) {
|
2016-06-07 19:16:52 +03:00
|
|
|
(void) fprintf(stderr, gettext("filesystem "
|
|
|
|
"successfully created, but it may only be "
|
|
|
|
"mounted by root\n"));
|
|
|
|
ret = 1;
|
|
|
|
} else if (zfs_mount(zhp, NULL, 0) != 0) {
|
2015-05-29 19:27:03 +03:00
|
|
|
(void) fprintf(stderr, gettext("filesystem "
|
|
|
|
"successfully created, but not mounted\n"));
|
|
|
|
ret = 1;
|
|
|
|
} else if (zfs_share(zhp) != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("filesystem "
|
|
|
|
"successfully created, but not shared\n"));
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
2008-12-03 23:09:06 +03:00
|
|
|
* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* Given an existing dataset, create a writable copy whose initial contents
|
|
|
|
* are the same as the source. The newly created dataset maintains a
|
|
|
|
* dependency on the original; the original cannot be destroyed so long as
|
|
|
|
* the clone exists.
|
|
|
|
*
|
|
|
|
* The '-p' flag creates all the non-existing ancestors of the target first.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_clone(int argc, char **argv)
|
|
|
|
{
|
2008-12-03 23:09:06 +03:00
|
|
|
zfs_handle_t *zhp = NULL;
|
2008-11-20 23:01:55 +03:00
|
|
|
boolean_t parents = B_FALSE;
|
2008-12-03 23:09:06 +03:00
|
|
|
nvlist_t *props;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
int c;
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
2008-12-03 23:09:06 +03:00
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/* check options */
|
2008-12-03 23:09:06 +03:00
|
|
|
while ((c = getopt(argc, argv, "o:p")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
2008-12-03 23:09:06 +03:00
|
|
|
case 'o':
|
2017-05-10 02:21:09 +03:00
|
|
|
if (!parseprop(props, optarg)) {
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(props);
|
2008-12-03 23:09:06 +03:00
|
|
|
return (1);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
2008-12-03 23:09:06 +03:00
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'p':
|
|
|
|
parents = B_TRUE;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
2008-12-03 23:09:06 +03:00
|
|
|
goto usage;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing source dataset "
|
|
|
|
"argument\n"));
|
2008-12-03 23:09:06 +03:00
|
|
|
goto usage;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
if (argc < 2) {
|
|
|
|
(void) fprintf(stderr, gettext("missing target dataset "
|
|
|
|
"argument\n"));
|
2008-12-03 23:09:06 +03:00
|
|
|
goto usage;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
if (argc > 2) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
2008-12-03 23:09:06 +03:00
|
|
|
goto usage;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* open the source dataset */
|
2016-09-01 05:23:10 +03:00
|
|
|
if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) {
|
|
|
|
nvlist_free(props);
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
|
|
|
|
ZFS_TYPE_VOLUME)) {
|
|
|
|
/*
|
|
|
|
* Now create the ancestors of the target dataset. If the
|
|
|
|
* target already exists and '-p' option was used we should not
|
|
|
|
* complain.
|
|
|
|
*/
|
|
|
|
if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
|
2016-09-01 05:23:10 +03:00
|
|
|
ZFS_TYPE_VOLUME)) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
nvlist_free(props);
|
2008-11-20 23:01:55 +03:00
|
|
|
return (0);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
|
|
|
if (zfs_create_ancestors(g_zfs, argv[1]) != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
nvlist_free(props);
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* pass to libzfs */
|
2008-12-03 23:09:06 +03:00
|
|
|
ret = zfs_clone(zhp, argv[1], props);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* create the mountpoint if necessary */
|
|
|
|
if (ret == 0) {
|
2013-12-24 00:06:34 +04:00
|
|
|
if (log_history) {
|
|
|
|
(void) zpool_log_history(g_zfs, history_str);
|
|
|
|
log_history = B_FALSE;
|
|
|
|
}
|
|
|
|
|
2015-05-29 19:27:03 +03:00
|
|
|
ret = zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
2008-12-03 23:09:06 +03:00
|
|
|
nvlist_free(props);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
return (!!ret);
|
|
|
|
|
|
|
|
usage:
|
2017-10-17 01:32:48 +03:00
|
|
|
ASSERT3P(zhp, ==, NULL);
|
2008-12-03 23:09:06 +03:00
|
|
|
nvlist_free(props);
|
|
|
|
usage(B_FALSE);
|
|
|
|
return (-1);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-07-16 21:19:24 +03:00
|
|
|
* zfs create [-Pnpv] [-o prop=value] ... fs
|
|
|
|
* zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* Create a new dataset. This command can be used to create filesystems
|
|
|
|
* and volumes. Snapshot creation is handled by 'zfs snapshot'.
|
|
|
|
* For volumes, the user must specify a size to be used.
|
|
|
|
*
|
|
|
|
* The '-s' flag applies only to volumes, and indicates that we should not try
|
|
|
|
* to set the reservation for this volume. By default we set a reservation
|
|
|
|
* equal to the size for any volume. For pools with SPA_VERSION >=
|
|
|
|
* SPA_VERSION_REFRESERVATION, we set a refreservation instead.
|
|
|
|
*
|
|
|
|
* The '-p' flag creates all the non-existing ancestors of the target first.
|
2019-07-16 21:19:24 +03:00
|
|
|
*
|
|
|
|
* The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity
|
|
|
|
* check of arguments and properties, but does not check for permissions,
|
|
|
|
* available space, etc.
|
|
|
|
*
|
|
|
|
* The '-v' flag is for verbose output.
|
|
|
|
*
|
|
|
|
* The '-P' flag is used for parseable output. It implies '-v'.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_create(int argc, char **argv)
|
|
|
|
{
|
|
|
|
zfs_type_t type = ZFS_TYPE_FILESYSTEM;
|
2019-07-16 21:19:24 +03:00
|
|
|
zpool_handle_t *zpool_handle = NULL;
|
|
|
|
nvlist_t *real_props = NULL;
|
2010-08-26 20:58:04 +04:00
|
|
|
uint64_t volsize = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
int c;
|
|
|
|
boolean_t noreserve = B_FALSE;
|
|
|
|
boolean_t bflag = B_FALSE;
|
|
|
|
boolean_t parents = B_FALSE;
|
2019-07-16 21:19:24 +03:00
|
|
|
boolean_t dryrun = B_FALSE;
|
|
|
|
boolean_t verbose = B_FALSE;
|
|
|
|
boolean_t parseable = B_FALSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
int ret = 1;
|
2008-12-03 23:09:06 +03:00
|
|
|
nvlist_t *props;
|
2008-11-20 23:01:55 +03:00
|
|
|
uint64_t intval;
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
2019-07-16 21:19:24 +03:00
|
|
|
while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
|
|
|
case 'V':
|
|
|
|
type = ZFS_TYPE_VOLUME;
|
|
|
|
if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("bad volume "
|
|
|
|
"size '%s': %s\n"), optarg,
|
|
|
|
libzfs_error_description(g_zfs));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nvlist_add_uint64(props,
|
2010-05-29 00:45:14 +04:00
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
volsize = intval;
|
|
|
|
break;
|
2019-07-16 21:19:24 +03:00
|
|
|
case 'P':
|
|
|
|
verbose = B_TRUE;
|
|
|
|
parseable = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'p':
|
|
|
|
parents = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
bflag = B_TRUE;
|
|
|
|
if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("bad volume "
|
|
|
|
"block size '%s': %s\n"), optarg,
|
|
|
|
libzfs_error_description(g_zfs));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nvlist_add_uint64(props,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
|
2010-05-29 00:45:14 +04:00
|
|
|
intval) != 0)
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2019-07-16 21:19:24 +03:00
|
|
|
case 'n':
|
|
|
|
dryrun = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'o':
|
2017-05-10 02:21:09 +03:00
|
|
|
if (!parseprop(props, optarg))
|
2008-11-20 23:01:55 +03:00
|
|
|
goto error;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
noreserve = B_TRUE;
|
|
|
|
break;
|
2019-07-16 21:19:24 +03:00
|
|
|
case 'v':
|
|
|
|
verbose = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing size "
|
|
|
|
"argument\n"));
|
|
|
|
goto badusage;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
goto badusage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
|
|
|
|
(void) fprintf(stderr, gettext("'-s' and '-b' can only be "
|
|
|
|
"used when creating a volume\n"));
|
|
|
|
goto badusage;
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc == 0) {
|
|
|
|
(void) fprintf(stderr, gettext("missing %s argument\n"),
|
|
|
|
zfs_type_to_name(type));
|
|
|
|
goto badusage;
|
|
|
|
}
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
goto badusage;
|
|
|
|
}
|
|
|
|
|
2019-07-16 21:19:24 +03:00
|
|
|
if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) {
|
|
|
|
char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
|
2008-11-20 23:01:55 +03:00
|
|
|
char *p;
|
|
|
|
|
2017-01-18 01:45:02 +03:00
|
|
|
if ((p = strchr(argv[0], '/')) != NULL)
|
2008-11-20 23:01:55 +03:00
|
|
|
*p = '\0';
|
|
|
|
zpool_handle = zpool_open(g_zfs, argv[0]);
|
|
|
|
if (p != NULL)
|
|
|
|
*p = '/';
|
|
|
|
if (zpool_handle == NULL)
|
|
|
|
goto error;
|
2016-01-14 01:37:39 +03:00
|
|
|
|
|
|
|
(void) snprintf(msg, sizeof (msg),
|
2019-07-16 21:19:24 +03:00
|
|
|
dryrun ? gettext("cannot verify '%s'") :
|
2016-01-14 01:37:39 +03:00
|
|
|
gettext("cannot create '%s'"), argv[0]);
|
|
|
|
if (props && (real_props = zfs_valid_proplist(g_zfs, type,
|
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 20:36:48 +03:00
|
|
|
props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {
|
2016-01-14 02:05:59 +03:00
|
|
|
zpool_close(zpool_handle);
|
2016-01-14 01:37:39 +03:00
|
|
|
goto error;
|
2016-01-14 02:05:59 +03:00
|
|
|
}
|
2019-07-16 21:19:24 +03:00
|
|
|
}
|
|
|
|
|
2020-04-25 05:04:34 +03:00
|
|
|
/*
|
|
|
|
* if volsize is not a multiple of volblocksize, round it up to the
|
|
|
|
* nearest multiple of the volblocksize
|
|
|
|
*/
|
|
|
|
if (type == ZFS_TYPE_VOLUME) {
|
|
|
|
uint64_t volblocksize;
|
|
|
|
|
|
|
|
if (nvlist_lookup_uint64(props,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
|
|
|
|
&volblocksize) != 0)
|
|
|
|
volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
|
|
|
|
|
|
|
|
if (volsize % volblocksize) {
|
|
|
|
volsize = P2ROUNDUP_TYPED(volsize, volblocksize,
|
|
|
|
uint64_t);
|
|
|
|
|
|
|
|
if (nvlist_add_uint64(props,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLSIZE), volsize) != 0) {
|
|
|
|
nvlist_free(props);
|
|
|
|
nomem();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-16 21:19:24 +03:00
|
|
|
if (type == ZFS_TYPE_VOLUME && !noreserve) {
|
|
|
|
uint64_t spa_version;
|
|
|
|
zfs_prop_t resv_prop;
|
|
|
|
char *strval;
|
|
|
|
|
|
|
|
spa_version = zpool_get_prop_int(zpool_handle,
|
|
|
|
ZPOOL_PROP_VERSION, NULL);
|
|
|
|
if (spa_version >= SPA_VERSION_REFRESERVATION)
|
|
|
|
resv_prop = ZFS_PROP_REFRESERVATION;
|
|
|
|
else
|
|
|
|
resv_prop = ZFS_PROP_RESERVATION;
|
2016-01-14 01:37:39 +03:00
|
|
|
|
2019-07-01 02:38:07 +03:00
|
|
|
volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
|
|
|
|
real_props);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
|
|
|
|
&strval) != 0) {
|
|
|
|
if (nvlist_add_uint64(props,
|
|
|
|
zfs_prop_to_name(resv_prop), volsize) != 0) {
|
|
|
|
nvlist_free(props);
|
2010-05-29 00:45:14 +04:00
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-16 21:19:24 +03:00
|
|
|
if (zpool_handle != NULL) {
|
|
|
|
zpool_close(zpool_handle);
|
|
|
|
nvlist_free(real_props);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (parents && zfs_name_valid(argv[0], type)) {
|
|
|
|
/*
|
|
|
|
* Now create the ancestors of target dataset. If the target
|
|
|
|
* already exists and '-p' option was used we should not
|
|
|
|
* complain.
|
|
|
|
*/
|
|
|
|
if (zfs_dataset_exists(g_zfs, argv[0], type)) {
|
|
|
|
ret = 0;
|
|
|
|
goto error;
|
|
|
|
}
|
2019-07-16 21:19:24 +03:00
|
|
|
if (verbose) {
|
|
|
|
(void) printf(parseable ? "create_ancestors\t%s\n" :
|
|
|
|
dryrun ? "would create ancestors of %s\n" :
|
|
|
|
"create ancestors of %s\n", argv[0]);
|
|
|
|
}
|
|
|
|
if (!dryrun) {
|
|
|
|
if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
nvpair_t *nvp = NULL;
|
|
|
|
(void) printf(parseable ? "create\t%s\n" :
|
|
|
|
dryrun ? "would create %s\n" : "create %s\n", argv[0]);
|
|
|
|
while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
|
|
|
|
uint64_t uval;
|
|
|
|
char *sval;
|
|
|
|
|
|
|
|
switch (nvpair_type(nvp)) {
|
|
|
|
case DATA_TYPE_UINT64:
|
|
|
|
VERIFY0(nvpair_value_uint64(nvp, &uval));
|
|
|
|
(void) printf(parseable ?
|
|
|
|
"property\t%s\t%llu\n" : "\t%s=%llu\n",
|
|
|
|
nvpair_name(nvp), (u_longlong_t)uval);
|
|
|
|
break;
|
|
|
|
case DATA_TYPE_STRING:
|
|
|
|
VERIFY0(nvpair_value_string(nvp, &sval));
|
|
|
|
(void) printf(parseable ?
|
|
|
|
"property\t%s\t%s\n" : "\t%s=%s\n",
|
|
|
|
nvpair_name(nvp), sval);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr, "property '%s' "
|
|
|
|
"has illegal type %d\n",
|
|
|
|
nvpair_name(nvp), nvpair_type(nvp));
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dryrun) {
|
|
|
|
ret = 0;
|
|
|
|
goto error;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* pass to libzfs */
|
|
|
|
if (zfs_create(g_zfs, argv[0], type, props) != 0)
|
|
|
|
goto error;
|
|
|
|
|
2013-12-24 00:06:34 +04:00
|
|
|
if (log_history) {
|
|
|
|
(void) zpool_log_history(g_zfs, history_str);
|
|
|
|
log_history = B_FALSE;
|
|
|
|
}
|
|
|
|
|
2015-05-29 19:27:03 +03:00
|
|
|
ret = zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);
|
2008-11-20 23:01:55 +03:00
|
|
|
error:
|
|
|
|
nvlist_free(props);
|
|
|
|
return (ret);
|
|
|
|
badusage:
|
|
|
|
nvlist_free(props);
|
|
|
|
usage(B_FALSE);
|
|
|
|
return (2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-29 00:45:14 +04:00
|
|
|
* zfs destroy [-rRf] <fs, vol>
|
|
|
|
* zfs destroy [-rRd] <snap>
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
2010-05-29 00:45:14 +04:00
|
|
|
* -r Recursively destroy all children
|
|
|
|
* -R Recursively destroy all dependents, including clones
|
|
|
|
* -f Force unmounting of any dependents
|
2009-08-18 22:43:27 +04:00
|
|
|
* -d If we can't destroy now, mark for deferred destruction
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* Destroys the given dataset. By default, it will unmount any filesystems,
|
|
|
|
* and refuse to destroy a dataset that has any dependents. A dependent can
|
|
|
|
* either be a child, or a clone of a child.
|
|
|
|
*/
|
|
|
|
typedef struct destroy_cbdata {
|
|
|
|
boolean_t cb_first;
|
2011-11-17 22:14:36 +04:00
|
|
|
boolean_t cb_force;
|
|
|
|
boolean_t cb_recurse;
|
|
|
|
boolean_t cb_error;
|
|
|
|
boolean_t cb_doclones;
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_handle_t *cb_target;
|
2009-08-18 22:43:27 +04:00
|
|
|
boolean_t cb_defer_destroy;
|
2011-11-17 22:14:36 +04:00
|
|
|
boolean_t cb_verbose;
|
|
|
|
boolean_t cb_parsable;
|
|
|
|
boolean_t cb_dryrun;
|
|
|
|
nvlist_t *cb_nvl;
|
2013-09-04 16:00:57 +04:00
|
|
|
nvlist_t *cb_batchedsnaps;
|
2011-11-17 22:14:36 +04:00
|
|
|
|
|
|
|
/* first snap in contiguous run */
|
2013-08-28 15:45:09 +04:00
|
|
|
char *cb_firstsnap;
|
2011-11-17 22:14:36 +04:00
|
|
|
/* previous snap in contiguous run */
|
2013-08-28 15:45:09 +04:00
|
|
|
char *cb_prevsnap;
|
2011-11-17 22:14:36 +04:00
|
|
|
int64_t cb_snapused;
|
|
|
|
char *cb_snapspec;
|
2013-12-12 02:33:41 +04:00
|
|
|
char *cb_bookmark;
|
2018-03-13 01:24:08 +03:00
|
|
|
uint64_t cb_snap_count;
|
2008-11-20 23:01:55 +03:00
|
|
|
} destroy_cbdata_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for any dependents based on the '-r' or '-R' flags.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
destroy_check_dependent(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
destroy_cbdata_t *cbp = data;
|
|
|
|
const char *tname = zfs_get_name(cbp->cb_target);
|
|
|
|
const char *name = zfs_get_name(zhp);
|
|
|
|
|
|
|
|
if (strncmp(tname, name, strlen(tname)) == 0 &&
|
|
|
|
(name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
|
|
|
|
/*
|
|
|
|
* This is a direct descendant, not a clone somewhere else in
|
|
|
|
* the hierarchy.
|
|
|
|
*/
|
|
|
|
if (cbp->cb_recurse)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (cbp->cb_first) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot destroy '%s': "
|
|
|
|
"%s has children\n"),
|
|
|
|
zfs_get_name(cbp->cb_target),
|
|
|
|
zfs_type_to_name(zfs_get_type(cbp->cb_target)));
|
|
|
|
(void) fprintf(stderr, gettext("use '-r' to destroy "
|
|
|
|
"the following datasets:\n"));
|
|
|
|
cbp->cb_first = B_FALSE;
|
2011-11-17 22:14:36 +04:00
|
|
|
cbp->cb_error = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This is a clone. We only want to report this if the '-r'
|
|
|
|
* wasn't specified, or the target is a snapshot.
|
|
|
|
*/
|
|
|
|
if (!cbp->cb_recurse &&
|
|
|
|
zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (cbp->cb_first) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot destroy '%s': "
|
|
|
|
"%s has dependent clones\n"),
|
|
|
|
zfs_get_name(cbp->cb_target),
|
|
|
|
zfs_type_to_name(zfs_get_type(cbp->cb_target)));
|
|
|
|
(void) fprintf(stderr, gettext("use '-R' to destroy "
|
|
|
|
"the following datasets:\n"));
|
|
|
|
cbp->cb_first = B_FALSE;
|
2011-11-17 22:14:36 +04:00
|
|
|
cbp->cb_error = B_TRUE;
|
|
|
|
cbp->cb_dryrun = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2018-03-13 01:24:08 +03:00
|
|
|
static int
|
|
|
|
destroy_batched(destroy_cbdata_t *cb)
|
|
|
|
{
|
|
|
|
int error = zfs_destroy_snaps_nvl(g_zfs,
|
|
|
|
cb->cb_batchedsnaps, B_FALSE);
|
|
|
|
fnvlist_free(cb->cb_batchedsnaps);
|
|
|
|
cb->cb_batchedsnaps = fnvlist_alloc();
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
static int
|
|
|
|
destroy_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
2011-11-17 22:14:36 +04:00
|
|
|
destroy_cbdata_t *cb = data;
|
|
|
|
const char *name = zfs_get_name(zhp);
|
2018-03-13 01:24:08 +03:00
|
|
|
int error;
|
2011-11-17 22:14:36 +04:00
|
|
|
|
|
|
|
if (cb->cb_verbose) {
|
|
|
|
if (cb->cb_parsable) {
|
|
|
|
(void) printf("destroy\t%s\n", name);
|
|
|
|
} else if (cb->cb_dryrun) {
|
|
|
|
(void) printf(gettext("would destroy %s\n"),
|
|
|
|
name);
|
|
|
|
} else {
|
|
|
|
(void) printf(gettext("will destroy %s\n"),
|
|
|
|
name);
|
|
|
|
}
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore pools (which we've already flagged as an error before getting
|
2010-05-29 00:45:14 +04:00
|
|
|
* here).
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
|
|
|
if (strchr(zfs_get_name(zhp), '/') == NULL &&
|
|
|
|
zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
2013-09-04 16:00:57 +04:00
|
|
|
if (cb->cb_dryrun) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We batch up all contiguous snapshots (even of different
|
|
|
|
* filesystems) and destroy them with one ioctl. We can't
|
|
|
|
* simply do all snap deletions and then all fs deletions,
|
|
|
|
* because we must delete a clone before its origin.
|
|
|
|
*/
|
|
|
|
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
|
2018-03-13 01:24:08 +03:00
|
|
|
cb->cb_snap_count++;
|
2013-09-04 16:00:57 +04:00
|
|
|
fnvlist_add_boolean(cb->cb_batchedsnaps, name);
|
2018-03-13 01:24:08 +03:00
|
|
|
if (cb->cb_snap_count % 10 == 0 && cb->cb_defer_destroy)
|
|
|
|
error = destroy_batched(cb);
|
2013-09-04 16:00:57 +04:00
|
|
|
} else {
|
2018-03-13 01:24:08 +03:00
|
|
|
error = destroy_batched(cb);
|
2013-09-04 16:00:57 +04:00
|
|
|
if (error != 0 ||
|
|
|
|
zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
|
2011-11-17 22:14:36 +04:00
|
|
|
zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
|
|
|
|
zfs_close(zhp);
|
2018-06-06 20:14:52 +03:00
|
|
|
/*
|
|
|
|
* When performing a recursive destroy we ignore errors
|
|
|
|
* so that the recursive destroy could continue
|
|
|
|
* destroying past problem datasets
|
|
|
|
*/
|
|
|
|
if (cb->cb_recurse) {
|
|
|
|
cb->cb_error = B_TRUE;
|
|
|
|
return (0);
|
|
|
|
}
|
2011-11-17 22:14:36 +04:00
|
|
|
return (-1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-11-17 22:14:36 +04:00
|
|
|
destroy_print_cb(zfs_handle_t *zhp, void *arg)
|
2008-11-20 23:01:55 +03:00
|
|
|
{
|
2011-11-17 22:14:36 +04:00
|
|
|
destroy_cbdata_t *cb = arg;
|
|
|
|
const char *name = zfs_get_name(zhp);
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (nvlist_exists(cb->cb_nvl, name)) {
|
|
|
|
if (cb->cb_firstsnap == NULL)
|
2013-08-28 15:45:09 +04:00
|
|
|
cb->cb_firstsnap = strdup(name);
|
2011-11-17 22:14:36 +04:00
|
|
|
if (cb->cb_prevsnap != NULL)
|
2013-08-28 15:45:09 +04:00
|
|
|
free(cb->cb_prevsnap);
|
2011-11-17 22:14:36 +04:00
|
|
|
/* this snap continues the current range */
|
2013-08-28 15:45:09 +04:00
|
|
|
cb->cb_prevsnap = strdup(name);
|
|
|
|
if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
|
|
|
|
nomem();
|
2011-11-17 22:14:36 +04:00
|
|
|
if (cb->cb_verbose) {
|
|
|
|
if (cb->cb_parsable) {
|
|
|
|
(void) printf("destroy\t%s\n", name);
|
|
|
|
} else if (cb->cb_dryrun) {
|
|
|
|
(void) printf(gettext("would destroy %s\n"),
|
|
|
|
name);
|
|
|
|
} else {
|
|
|
|
(void) printf(gettext("will destroy %s\n"),
|
|
|
|
name);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2011-11-17 22:14:36 +04:00
|
|
|
} else if (cb->cb_firstsnap != NULL) {
|
|
|
|
/* end of this range */
|
|
|
|
uint64_t used = 0;
|
2013-08-28 15:45:09 +04:00
|
|
|
err = lzc_snaprange_space(cb->cb_firstsnap,
|
2011-11-17 22:14:36 +04:00
|
|
|
cb->cb_prevsnap, &used);
|
|
|
|
cb->cb_snapused += used;
|
2013-08-28 15:45:09 +04:00
|
|
|
free(cb->cb_firstsnap);
|
2011-11-17 22:14:36 +04:00
|
|
|
cb->cb_firstsnap = NULL;
|
2013-08-28 15:45:09 +04:00
|
|
|
free(cb->cb_prevsnap);
|
2011-11-17 22:14:36 +04:00
|
|
|
cb->cb_prevsnap = NULL;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2011-11-17 22:14:36 +04:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (err);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
static int
|
|
|
|
destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
assert(cb->cb_firstsnap == NULL);
|
|
|
|
assert(cb->cb_prevsnap == NULL);
|
2019-03-12 23:13:22 +03:00
|
|
|
err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb, 0, 0);
|
2011-11-17 22:14:36 +04:00
|
|
|
if (cb->cb_firstsnap != NULL) {
|
|
|
|
uint64_t used = 0;
|
|
|
|
if (err == 0) {
|
2013-08-28 15:45:09 +04:00
|
|
|
err = lzc_snaprange_space(cb->cb_firstsnap,
|
2011-11-17 22:14:36 +04:00
|
|
|
cb->cb_prevsnap, &used);
|
|
|
|
}
|
|
|
|
cb->cb_snapused += used;
|
2013-08-28 15:45:09 +04:00
|
|
|
free(cb->cb_firstsnap);
|
2011-11-17 22:14:36 +04:00
|
|
|
cb->cb_firstsnap = NULL;
|
2013-08-28 15:45:09 +04:00
|
|
|
free(cb->cb_prevsnap);
|
2011-11-17 22:14:36 +04:00
|
|
|
cb->cb_prevsnap = NULL;
|
|
|
|
}
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
destroy_cbdata_t *cb = arg;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
/* Check for clones. */
|
2012-07-12 16:32:45 +04:00
|
|
|
if (!cb->cb_doclones && !cb->cb_defer_destroy) {
|
2011-11-17 22:14:36 +04:00
|
|
|
cb->cb_target = zhp;
|
|
|
|
cb->cb_first = B_TRUE;
|
|
|
|
err = zfs_iter_dependents(zhp, B_TRUE,
|
|
|
|
destroy_check_dependent, cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
|
|
|
|
nomem();
|
|
|
|
}
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
gather_snapshots(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
destroy_cbdata_t *cb = arg;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb);
|
|
|
|
if (err == ENOENT)
|
|
|
|
err = 0;
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (cb->cb_verbose) {
|
|
|
|
err = destroy_print_snapshots(zhp, cb);
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb->cb_recurse)
|
|
|
|
err = zfs_iter_filesystems(zhp, gather_snapshots, cb);
|
|
|
|
|
|
|
|
out:
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
destroy_clones(destroy_cbdata_t *cb)
|
|
|
|
{
|
|
|
|
nvpair_t *pair;
|
|
|
|
for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
|
|
|
|
pair != NULL;
|
|
|
|
pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
|
|
|
|
zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
|
|
|
|
ZFS_TYPE_SNAPSHOT);
|
|
|
|
if (zhp != NULL) {
|
|
|
|
boolean_t defer = cb->cb_defer_destroy;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can't defer destroy non-snapshots, so set it to
|
|
|
|
* false while destroying the clones.
|
|
|
|
*/
|
|
|
|
cb->cb_defer_destroy = B_FALSE;
|
|
|
|
err = zfs_iter_dependents(zhp, B_FALSE,
|
|
|
|
destroy_callback, cb);
|
|
|
|
cb->cb_defer_destroy = defer;
|
|
|
|
zfs_close(zhp);
|
|
|
|
if (err != 0)
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_destroy(int argc, char **argv)
|
|
|
|
{
|
|
|
|
destroy_cbdata_t cb = { 0 };
|
2013-09-04 16:00:57 +04:00
|
|
|
int rv = 0;
|
|
|
|
int err = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
int c;
|
2013-09-04 16:00:57 +04:00
|
|
|
zfs_handle_t *zhp = NULL;
|
2013-12-12 02:33:41 +04:00
|
|
|
char *at, *pound;
|
2010-05-29 00:45:14 +04:00
|
|
|
zfs_type_t type = ZFS_TYPE_DATASET;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
2011-11-17 22:14:36 +04:00
|
|
|
while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
2011-11-17 22:14:36 +04:00
|
|
|
case 'v':
|
|
|
|
cb.cb_verbose = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
cb.cb_verbose = B_TRUE;
|
|
|
|
cb.cb_parsable = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
cb.cb_dryrun = B_TRUE;
|
|
|
|
break;
|
2009-08-18 22:43:27 +04:00
|
|
|
case 'd':
|
|
|
|
cb.cb_defer_destroy = B_TRUE;
|
2010-05-29 00:45:14 +04:00
|
|
|
type = ZFS_TYPE_SNAPSHOT;
|
2009-08-18 22:43:27 +04:00
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'f':
|
2011-11-17 22:14:36 +04:00
|
|
|
cb.cb_force = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 'r':
|
2011-11-17 22:14:36 +04:00
|
|
|
cb.cb_recurse = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 'R':
|
2011-11-17 22:14:36 +04:00
|
|
|
cb.cb_recurse = B_TRUE;
|
|
|
|
cb.cb_doclones = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc == 0) {
|
2011-11-17 22:14:36 +04:00
|
|
|
(void) fprintf(stderr, gettext("missing dataset argument\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
at = strchr(argv[0], '@');
|
2013-12-12 02:33:41 +04:00
|
|
|
pound = strchr(argv[0], '#');
|
2011-11-17 22:14:36 +04:00
|
|
|
if (at != NULL) {
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
/* Build the list of snaps to destroy in cb_nvl. */
|
2013-09-04 16:00:57 +04:00
|
|
|
cb.cb_nvl = fnvlist_alloc();
|
2011-11-17 22:14:36 +04:00
|
|
|
|
|
|
|
*at = '\0';
|
|
|
|
zhp = zfs_open(g_zfs, argv[0],
|
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
2016-09-01 05:23:10 +03:00
|
|
|
if (zhp == NULL) {
|
|
|
|
nvlist_free(cb.cb_nvl);
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
cb.cb_snapspec = at + 1;
|
|
|
|
if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
|
|
|
|
cb.cb_error) {
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
|
|
|
goto out;
|
2011-11-17 22:14:36 +04:00
|
|
|
}
|
2010-05-29 00:45:14 +04:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
if (nvlist_empty(cb.cb_nvl)) {
|
|
|
|
(void) fprintf(stderr, gettext("could not find any "
|
|
|
|
"snapshots to destroy; check snapshot names.\n"));
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
|
|
|
goto out;
|
2011-11-17 22:14:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cb.cb_verbose) {
|
|
|
|
char buf[16];
|
2017-05-02 23:43:53 +03:00
|
|
|
zfs_nicebytes(cb.cb_snapused, buf, sizeof (buf));
|
2011-11-17 22:14:36 +04:00
|
|
|
if (cb.cb_parsable) {
|
|
|
|
(void) printf("reclaim\t%llu\n",
|
|
|
|
(u_longlong_t)cb.cb_snapused);
|
|
|
|
} else if (cb.cb_dryrun) {
|
|
|
|
(void) printf(gettext("would reclaim %s\n"),
|
|
|
|
buf);
|
|
|
|
} else {
|
|
|
|
(void) printf(gettext("will reclaim %s\n"),
|
|
|
|
buf);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
if (!cb.cb_dryrun) {
|
2013-09-04 16:00:57 +04:00
|
|
|
if (cb.cb_doclones) {
|
|
|
|
cb.cb_batchedsnaps = fnvlist_alloc();
|
2011-11-17 22:14:36 +04:00
|
|
|
err = destroy_clones(&cb);
|
2013-09-04 16:00:57 +04:00
|
|
|
if (err == 0) {
|
|
|
|
err = zfs_destroy_snaps_nvl(g_zfs,
|
|
|
|
cb.cb_batchedsnaps, B_FALSE);
|
|
|
|
}
|
|
|
|
if (err != 0) {
|
|
|
|
rv = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2011-11-17 22:14:36 +04:00
|
|
|
if (err == 0) {
|
2013-09-04 16:00:57 +04:00
|
|
|
err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
|
2011-11-17 22:14:36 +04:00
|
|
|
cb.cb_defer_destroy);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
if (err != 0)
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
2013-12-12 02:33:41 +04:00
|
|
|
} else if (pound != NULL) {
|
|
|
|
int err;
|
|
|
|
nvlist_t *nvl;
|
|
|
|
|
|
|
|
if (cb.cb_dryrun) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
"dryrun is not supported with bookmark\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb.cb_defer_destroy) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
"defer destroy is not supported with bookmark\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb.cb_recurse) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
"recursive is not supported with bookmark\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
/*
|
|
|
|
* Unfortunately, zfs_bookmark() doesn't honor the
|
|
|
|
* casesensitivity setting. However, we can't simply
|
|
|
|
* remove this check, because lzc_destroy_bookmarks()
|
|
|
|
* ignores non-existent bookmarks, so this is necessary
|
|
|
|
* to get a proper error message.
|
|
|
|
*/
|
2013-12-12 02:33:41 +04:00
|
|
|
if (!zfs_bookmark_exists(argv[0])) {
|
|
|
|
(void) fprintf(stderr, gettext("bookmark '%s' "
|
|
|
|
"does not exist.\n"), argv[0]);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
nvl = fnvlist_alloc();
|
|
|
|
fnvlist_add_boolean(nvl, argv[0]);
|
|
|
|
|
|
|
|
err = lzc_destroy_bookmarks(nvl, NULL);
|
|
|
|
if (err != 0) {
|
|
|
|
(void) zfs_standard_error(g_zfs, err,
|
|
|
|
"cannot destroy bookmark");
|
|
|
|
}
|
|
|
|
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(nvl);
|
2013-12-12 02:33:41 +04:00
|
|
|
|
|
|
|
return (err);
|
2011-11-17 22:14:36 +04:00
|
|
|
} else {
|
|
|
|
/* Open the given dataset */
|
|
|
|
if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
|
|
|
|
return (1);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
cb.cb_target = zhp;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
/*
|
|
|
|
* Perform an explicit check for pools before going any further.
|
|
|
|
*/
|
|
|
|
if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
|
|
|
|
zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot destroy '%s': "
|
|
|
|
"operation does not apply to pools\n"),
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
(void) fprintf(stderr, gettext("use 'zfs destroy -r "
|
|
|
|
"%s' to destroy all datasets in the pool\n"),
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
(void) fprintf(stderr, gettext("use 'zpool destroy %s' "
|
|
|
|
"to destroy the pool itself\n"), zfs_get_name(zhp));
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
|
|
|
goto out;
|
2011-11-17 22:14:36 +04:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
/*
|
|
|
|
* Check for any dependents and/or clones.
|
|
|
|
*/
|
|
|
|
cb.cb_first = B_TRUE;
|
|
|
|
if (!cb.cb_doclones &&
|
|
|
|
zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
|
|
|
|
&cb) != 0) {
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
|
|
|
goto out;
|
2011-11-17 22:14:36 +04:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
if (cb.cb_error) {
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
|
|
|
goto out;
|
2011-11-17 22:14:36 +04:00
|
|
|
}
|
2013-09-04 16:00:57 +04:00
|
|
|
cb.cb_batchedsnaps = fnvlist_alloc();
|
2011-11-17 22:14:36 +04:00
|
|
|
if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
|
|
|
|
&cb) != 0) {
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
|
|
|
goto out;
|
2011-11-17 22:14:36 +04:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
/*
|
|
|
|
* Do the real thing. The callback will close the
|
|
|
|
* handle regardless of whether it succeeds or not.
|
|
|
|
*/
|
2013-09-04 16:00:57 +04:00
|
|
|
err = destroy_callback(zhp, &cb);
|
|
|
|
zhp = NULL;
|
|
|
|
if (err == 0) {
|
|
|
|
err = zfs_destroy_snaps_nvl(g_zfs,
|
|
|
|
cb.cb_batchedsnaps, cb.cb_defer_destroy);
|
|
|
|
}
|
2018-06-06 20:14:52 +03:00
|
|
|
if (err != 0 || cb.cb_error == B_TRUE)
|
2013-09-04 16:00:57 +04:00
|
|
|
rv = 1;
|
2011-11-17 22:14:36 +04:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2013-09-04 16:00:57 +04:00
|
|
|
out:
|
|
|
|
fnvlist_free(cb.cb_batchedsnaps);
|
|
|
|
fnvlist_free(cb.cb_nvl);
|
|
|
|
if (zhp != NULL)
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (rv);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
static boolean_t
|
|
|
|
is_recvd_column(zprop_get_cbdata_t *cbp)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
zfs_get_column_t col;
|
|
|
|
|
|
|
|
for (i = 0; i < ZFS_GET_NCOLS &&
|
|
|
|
(col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
|
|
|
|
if (col == GET_COL_RECVD)
|
|
|
|
return (B_TRUE);
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
2010-05-29 00:45:14 +04:00
|
|
|
* zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
|
|
|
|
* < all | property[,property]... > < fs | snap | vol > ...
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* -r recurse over any child datasets
|
|
|
|
* -H scripted mode. Headers are stripped, and fields are separated
|
|
|
|
* by tabs instead of spaces.
|
2010-05-29 00:45:14 +04:00
|
|
|
* -o Set of fields to display. One of "name,property,value,
|
|
|
|
* received,source". Default is "name,property,value,source".
|
|
|
|
* "all" is an alias for all five.
|
2008-11-20 23:01:55 +03:00
|
|
|
* -s Set of sources to allow. One of
|
2010-05-29 00:45:14 +04:00
|
|
|
* "local,default,inherited,received,temporary,none". Default is
|
|
|
|
* all six.
|
2008-11-20 23:01:55 +03:00
|
|
|
* -p Display values in parsable (literal) format.
|
|
|
|
*
|
|
|
|
* Prints properties for the given datasets. The user can control which
|
|
|
|
* columns to display as well as which property types to allow.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Invoked to display the properties for a single dataset.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
get_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
char buf[ZFS_MAXPROPLEN];
|
2010-05-29 00:45:14 +04:00
|
|
|
char rbuf[ZFS_MAXPROPLEN];
|
2008-11-20 23:01:55 +03:00
|
|
|
zprop_source_t sourcetype;
|
2016-06-16 00:28:36 +03:00
|
|
|
char source[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 23:01:55 +03:00
|
|
|
zprop_get_cbdata_t *cbp = data;
|
2010-05-29 00:45:14 +04:00
|
|
|
nvlist_t *user_props = zfs_get_user_props(zhp);
|
2008-11-20 23:01:55 +03:00
|
|
|
zprop_list_t *pl = cbp->cb_proplist;
|
|
|
|
nvlist_t *propval;
|
|
|
|
char *strval;
|
|
|
|
char *sourceval;
|
2010-05-29 00:45:14 +04:00
|
|
|
boolean_t received = is_recvd_column(cbp);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
for (; pl != NULL; pl = pl->pl_next) {
|
2010-05-29 00:45:14 +04:00
|
|
|
char *recvdval = NULL;
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
|
|
|
* Skip the special fake placeholder. This will also skip over
|
|
|
|
* the name property when 'all' is specified.
|
|
|
|
*/
|
|
|
|
if (pl->pl_prop == ZFS_PROP_NAME &&
|
|
|
|
pl == cbp->cb_proplist)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pl->pl_prop != ZPROP_INVAL) {
|
|
|
|
if (zfs_prop_get(zhp, pl->pl_prop, buf,
|
|
|
|
sizeof (buf), &sourcetype, source,
|
|
|
|
sizeof (source),
|
|
|
|
cbp->cb_literal) != 0) {
|
|
|
|
if (pl->pl_all)
|
|
|
|
continue;
|
|
|
|
if (!zfs_prop_valid_for_type(pl->pl_prop,
|
2014-04-21 22:22:08 +04:00
|
|
|
ZFS_TYPE_DATASET, B_FALSE)) {
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("No such property '%s'\n"),
|
|
|
|
zfs_prop_to_name(pl->pl_prop));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sourcetype = ZPROP_SRC_NONE;
|
|
|
|
(void) strlcpy(buf, "-", sizeof (buf));
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (received && (zfs_prop_get_recvd(zhp,
|
|
|
|
zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
|
|
|
|
cbp->cb_literal) == 0))
|
|
|
|
recvdval = rbuf;
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
zprop_print_one_property(zfs_get_name(zhp), cbp,
|
|
|
|
zfs_prop_to_name(pl->pl_prop),
|
2010-05-29 00:45:14 +04:00
|
|
|
buf, sourcetype, source, recvdval);
|
2009-07-03 02:44:48 +04:00
|
|
|
} else if (zfs_prop_userquota(pl->pl_user_prop)) {
|
|
|
|
sourcetype = ZPROP_SRC_LOCAL;
|
|
|
|
|
|
|
|
if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
|
|
|
|
buf, sizeof (buf), cbp->cb_literal) != 0) {
|
|
|
|
sourcetype = ZPROP_SRC_NONE;
|
|
|
|
(void) strlcpy(buf, "-", sizeof (buf));
|
|
|
|
}
|
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
zprop_print_one_property(zfs_get_name(zhp), cbp,
|
|
|
|
pl->pl_user_prop, buf, sourcetype, source, NULL);
|
|
|
|
} else if (zfs_prop_written(pl->pl_user_prop)) {
|
|
|
|
sourcetype = ZPROP_SRC_LOCAL;
|
|
|
|
|
|
|
|
if (zfs_prop_get_written(zhp, pl->pl_user_prop,
|
|
|
|
buf, sizeof (buf), cbp->cb_literal) != 0) {
|
|
|
|
sourcetype = ZPROP_SRC_NONE;
|
|
|
|
(void) strlcpy(buf, "-", sizeof (buf));
|
|
|
|
}
|
|
|
|
|
2009-07-03 02:44:48 +04:00
|
|
|
zprop_print_one_property(zfs_get_name(zhp), cbp,
|
2010-05-29 00:45:14 +04:00
|
|
|
pl->pl_user_prop, buf, sourcetype, source, NULL);
|
2008-11-20 23:01:55 +03:00
|
|
|
} else {
|
2010-05-29 00:45:14 +04:00
|
|
|
if (nvlist_lookup_nvlist(user_props,
|
2008-11-20 23:01:55 +03:00
|
|
|
pl->pl_user_prop, &propval) != 0) {
|
|
|
|
if (pl->pl_all)
|
|
|
|
continue;
|
|
|
|
sourcetype = ZPROP_SRC_NONE;
|
|
|
|
strval = "-";
|
|
|
|
} else {
|
|
|
|
verify(nvlist_lookup_string(propval,
|
|
|
|
ZPROP_VALUE, &strval) == 0);
|
|
|
|
verify(nvlist_lookup_string(propval,
|
|
|
|
ZPROP_SOURCE, &sourceval) == 0);
|
|
|
|
|
|
|
|
if (strcmp(sourceval,
|
|
|
|
zfs_get_name(zhp)) == 0) {
|
|
|
|
sourcetype = ZPROP_SRC_LOCAL;
|
2010-05-29 00:45:14 +04:00
|
|
|
} else if (strcmp(sourceval,
|
|
|
|
ZPROP_SOURCE_VAL_RECVD) == 0) {
|
|
|
|
sourcetype = ZPROP_SRC_RECEIVED;
|
2008-11-20 23:01:55 +03:00
|
|
|
} else {
|
|
|
|
sourcetype = ZPROP_SRC_INHERITED;
|
|
|
|
(void) strlcpy(source,
|
|
|
|
sourceval, sizeof (source));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (received && (zfs_prop_get_recvd(zhp,
|
|
|
|
pl->pl_user_prop, rbuf, sizeof (rbuf),
|
|
|
|
cbp->cb_literal) == 0))
|
|
|
|
recvdval = rbuf;
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
zprop_print_one_property(zfs_get_name(zhp), cbp,
|
|
|
|
pl->pl_user_prop, strval, sourcetype,
|
2010-05-29 00:45:14 +04:00
|
|
|
source, recvdval);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_get(int argc, char **argv)
|
|
|
|
{
|
|
|
|
zprop_get_cbdata_t cb = { 0 };
|
2011-07-26 22:53:09 +04:00
|
|
|
int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
|
2017-01-27 01:42:15 +03:00
|
|
|
int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;
|
2008-11-20 23:01:55 +03:00
|
|
|
char *value, *fields;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2009-07-03 02:44:48 +04:00
|
|
|
int limit = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
zprop_list_t fake_name = { 0 };
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up default columns and sources.
|
|
|
|
*/
|
|
|
|
cb.cb_sources = ZPROP_SRC_ALL;
|
|
|
|
cb.cb_columns[0] = GET_COL_NAME;
|
|
|
|
cb.cb_columns[1] = GET_COL_PROPERTY;
|
|
|
|
cb.cb_columns[2] = GET_COL_VALUE;
|
|
|
|
cb.cb_columns[3] = GET_COL_SOURCE;
|
|
|
|
cb.cb_type = ZFS_TYPE_DATASET;
|
|
|
|
|
|
|
|
/* check options */
|
2012-02-17 02:14:36 +04:00
|
|
|
while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
|
|
|
case 'p':
|
|
|
|
cb.cb_literal = B_TRUE;
|
|
|
|
break;
|
2009-07-03 02:44:48 +04:00
|
|
|
case 'd':
|
|
|
|
limit = parse_depth(optarg, &flags);
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'r':
|
2008-12-03 23:09:06 +03:00
|
|
|
flags |= ZFS_ITER_RECURSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
cb.cb_scripted = B_TRUE;
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing argument for "
|
|
|
|
"'%c' option\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
/*
|
|
|
|
* Process the set of columns to display. We zero out
|
|
|
|
* the structure to give us a blank slate.
|
|
|
|
*/
|
|
|
|
bzero(&cb.cb_columns, sizeof (cb.cb_columns));
|
|
|
|
i = 0;
|
|
|
|
while (*optarg != '\0') {
|
|
|
|
static char *col_subopts[] =
|
2010-05-29 00:45:14 +04:00
|
|
|
{ "name", "property", "value", "received",
|
|
|
|
"source", "all", NULL };
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (i == ZFS_GET_NCOLS) {
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(stderr, gettext("too "
|
|
|
|
"many fields given to -o "
|
|
|
|
"option\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (getsubopt(&optarg, col_subopts,
|
|
|
|
&value)) {
|
|
|
|
case 0:
|
|
|
|
cb.cb_columns[i++] = GET_COL_NAME;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
cb.cb_columns[i++] = GET_COL_PROPERTY;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
cb.cb_columns[i++] = GET_COL_VALUE;
|
|
|
|
break;
|
|
|
|
case 3:
|
2010-05-29 00:45:14 +04:00
|
|
|
cb.cb_columns[i++] = GET_COL_RECVD;
|
|
|
|
flags |= ZFS_ITER_RECVD_PROPS;
|
|
|
|
break;
|
|
|
|
case 4:
|
2008-11-20 23:01:55 +03:00
|
|
|
cb.cb_columns[i++] = GET_COL_SOURCE;
|
|
|
|
break;
|
2010-05-29 00:45:14 +04:00
|
|
|
case 5:
|
|
|
|
if (i > 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("\"all\" conflicts "
|
|
|
|
"with specific fields "
|
|
|
|
"given to -o option\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
cb.cb_columns[0] = GET_COL_NAME;
|
|
|
|
cb.cb_columns[1] = GET_COL_PROPERTY;
|
|
|
|
cb.cb_columns[2] = GET_COL_VALUE;
|
|
|
|
cb.cb_columns[3] = GET_COL_RECVD;
|
|
|
|
cb.cb_columns[4] = GET_COL_SOURCE;
|
|
|
|
flags |= ZFS_ITER_RECVD_PROPS;
|
|
|
|
i = ZFS_GET_NCOLS;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
default:
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid column name "
|
|
|
|
"'%s'\n"), value);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
cb.cb_sources = 0;
|
|
|
|
while (*optarg != '\0') {
|
|
|
|
static char *source_subopts[] = {
|
|
|
|
"local", "default", "inherited",
|
2010-05-29 00:45:14 +04:00
|
|
|
"received", "temporary", "none",
|
|
|
|
NULL };
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
switch (getsubopt(&optarg, source_subopts,
|
|
|
|
&value)) {
|
|
|
|
case 0:
|
|
|
|
cb.cb_sources |= ZPROP_SRC_LOCAL;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
cb.cb_sources |= ZPROP_SRC_DEFAULT;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
cb.cb_sources |= ZPROP_SRC_INHERITED;
|
|
|
|
break;
|
|
|
|
case 3:
|
2010-05-29 00:45:14 +04:00
|
|
|
cb.cb_sources |= ZPROP_SRC_RECEIVED;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 4:
|
2010-05-29 00:45:14 +04:00
|
|
|
cb.cb_sources |= ZPROP_SRC_TEMPORARY;
|
|
|
|
break;
|
|
|
|
case 5:
|
2008-11-20 23:01:55 +03:00
|
|
|
cb.cb_sources |= ZPROP_SRC_NONE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid source "
|
|
|
|
"'%s'\n"), value);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-02-17 02:14:36 +04:00
|
|
|
case 't':
|
|
|
|
types = 0;
|
|
|
|
flags &= ~ZFS_ITER_PROP_LISTSNAPS;
|
|
|
|
while (*optarg != '\0') {
|
|
|
|
static char *type_subopts[] = { "filesystem",
|
2019-11-19 03:44:28 +03:00
|
|
|
"volume", "snapshot", "snap", "bookmark",
|
2013-12-12 02:33:41 +04:00
|
|
|
"all", NULL };
|
2012-02-17 02:14:36 +04:00
|
|
|
|
|
|
|
switch (getsubopt(&optarg, type_subopts,
|
|
|
|
&value)) {
|
|
|
|
case 0:
|
|
|
|
types |= ZFS_TYPE_FILESYSTEM;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
types |= ZFS_TYPE_VOLUME;
|
|
|
|
break;
|
|
|
|
case 2:
|
2019-11-19 03:44:28 +03:00
|
|
|
case 3:
|
2012-02-17 02:14:36 +04:00
|
|
|
types |= ZFS_TYPE_SNAPSHOT;
|
|
|
|
break;
|
2019-11-19 03:44:28 +03:00
|
|
|
case 4:
|
2013-12-12 02:33:41 +04:00
|
|
|
types |= ZFS_TYPE_BOOKMARK;
|
|
|
|
break;
|
2019-11-19 03:44:28 +03:00
|
|
|
case 5:
|
2013-12-12 02:33:41 +04:00
|
|
|
types = ZFS_TYPE_DATASET |
|
|
|
|
ZFS_TYPE_BOOKMARK;
|
2012-02-17 02:14:36 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid type '%s'\n"),
|
|
|
|
value);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing property "
|
|
|
|
"argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
fields = argv[0];
|
|
|
|
|
2019-04-01 21:58:59 +03:00
|
|
|
/*
|
2019-11-19 03:44:28 +03:00
|
|
|
* Handle users who want to get all snapshots or bookmarks
|
|
|
|
* of a dataset (ex. 'zfs get -t snapshot refer <dataset>').
|
2019-04-01 21:58:59 +03:00
|
|
|
*/
|
2019-11-19 03:44:28 +03:00
|
|
|
if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&
|
|
|
|
argc > 1 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {
|
2019-04-01 21:58:59 +03:00
|
|
|
flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);
|
|
|
|
limit = 1;
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
|
|
|
|
!= 0)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As part of zfs_expand_proplist(), we keep track of the maximum column
|
|
|
|
* width for each property. For the 'NAME' (and 'SOURCE') columns, we
|
|
|
|
* need to know the maximum name length. However, the user likely did
|
|
|
|
* not specify 'name' as one of the properties to fetch, so we need to
|
|
|
|
* make sure we always include at least this property for
|
|
|
|
* print_get_headers() to work properly.
|
|
|
|
*/
|
|
|
|
if (cb.cb_proplist != NULL) {
|
|
|
|
fake_name.pl_prop = ZFS_PROP_NAME;
|
|
|
|
fake_name.pl_width = strlen(gettext("NAME"));
|
|
|
|
fake_name.pl_next = cb.cb_proplist;
|
|
|
|
cb.cb_proplist = &fake_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
cb.cb_first = B_TRUE;
|
|
|
|
|
|
|
|
/* run for each object */
|
2012-02-17 02:14:36 +04:00
|
|
|
ret = zfs_for_each(argc, argv, flags, types, NULL,
|
2009-07-03 02:44:48 +04:00
|
|
|
&cb.cb_proplist, limit, get_callback, &cb);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (cb.cb_proplist == &fake_name)
|
|
|
|
zprop_free_list(fake_name.pl_next);
|
|
|
|
else
|
|
|
|
zprop_free_list(cb.cb_proplist);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-29 00:45:14 +04:00
|
|
|
* inherit [-rS] <property> <fs|vol> ...
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
2010-05-29 00:45:14 +04:00
|
|
|
* -r Recurse over all children
|
|
|
|
* -S Revert to received value, if any
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* For each dataset specified on the command line, inherit the given property
|
|
|
|
* from its parent. Inheriting a property at the pool level will cause it to
|
|
|
|
* use the default value. The '-r' flag will recurse over all children, and is
|
|
|
|
* useful for setting a property on a hierarchy-wide basis, regardless of any
|
|
|
|
* local modifications for each dataset.
|
|
|
|
*/
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
typedef struct inherit_cbdata {
|
|
|
|
const char *cb_propname;
|
|
|
|
boolean_t cb_received;
|
|
|
|
} inherit_cbdata_t;
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
static int
|
2008-12-03 23:09:06 +03:00
|
|
|
inherit_recurse_cb(zfs_handle_t *zhp, void *data)
|
2008-11-20 23:01:55 +03:00
|
|
|
{
|
2010-05-29 00:45:14 +04:00
|
|
|
inherit_cbdata_t *cb = data;
|
|
|
|
zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
/*
|
|
|
|
* If we're doing it recursively, then ignore properties that
|
|
|
|
* are not valid for this type of dataset.
|
|
|
|
*/
|
|
|
|
if (prop != ZPROP_INVAL &&
|
2014-04-21 22:22:08 +04:00
|
|
|
!zfs_prop_valid_for_type(prop, zfs_get_type(zhp), B_FALSE))
|
2008-12-03 23:09:06 +03:00
|
|
|
return (0);
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
|
2008-12-03 23:09:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
inherit_cb(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
2010-05-29 00:45:14 +04:00
|
|
|
inherit_cbdata_t *cb = data;
|
2008-12-03 23:09:06 +03:00
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_inherit(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
zfs_prop_t prop;
|
2010-05-29 00:45:14 +04:00
|
|
|
inherit_cbdata_t cb = { 0 };
|
2008-11-20 23:01:55 +03:00
|
|
|
char *propname;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-12-03 23:09:06 +03:00
|
|
|
int flags = 0;
|
2010-05-29 00:45:14 +04:00
|
|
|
boolean_t received = B_FALSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
2010-05-29 00:45:14 +04:00
|
|
|
while ((c = getopt(argc, argv, "rS")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
2008-12-03 23:09:06 +03:00
|
|
|
flags |= ZFS_ITER_RECURSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2010-05-29 00:45:14 +04:00
|
|
|
case 'S':
|
|
|
|
received = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing property argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc < 2) {
|
|
|
|
(void) fprintf(stderr, gettext("missing dataset argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
propname = argv[0];
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
|
|
|
|
if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
|
|
|
|
if (zfs_prop_readonly(prop)) {
|
|
|
|
(void) fprintf(stderr, gettext(
|
|
|
|
"%s property is read-only\n"),
|
|
|
|
propname);
|
|
|
|
return (1);
|
|
|
|
}
|
2010-05-29 00:45:14 +04:00
|
|
|
if (!zfs_prop_inheritable(prop) && !received) {
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(stderr, gettext("'%s' property cannot "
|
|
|
|
"be inherited\n"), propname);
|
|
|
|
if (prop == ZFS_PROP_QUOTA ||
|
|
|
|
prop == ZFS_PROP_RESERVATION ||
|
|
|
|
prop == ZFS_PROP_REFQUOTA ||
|
2015-01-01 04:44:52 +03:00
|
|
|
prop == ZFS_PROP_REFRESERVATION) {
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(stderr, gettext("use 'zfs set "
|
|
|
|
"%s=none' to clear\n"), propname);
|
2015-01-01 04:44:52 +03:00
|
|
|
(void) fprintf(stderr, gettext("use 'zfs "
|
|
|
|
"inherit -S %s' to revert to received "
|
|
|
|
"value\n"), propname);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
|
|
|
}
|
2010-05-29 00:45:14 +04:00
|
|
|
if (received && (prop == ZFS_PROP_VOLSIZE ||
|
|
|
|
prop == ZFS_PROP_VERSION)) {
|
|
|
|
(void) fprintf(stderr, gettext("'%s' property cannot "
|
|
|
|
"be reverted to a received value\n"), propname);
|
|
|
|
return (1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
} else if (!zfs_prop_user(propname)) {
|
|
|
|
(void) fprintf(stderr, gettext("invalid property '%s'\n"),
|
|
|
|
propname);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
cb.cb_propname = propname;
|
|
|
|
cb.cb_received = received;
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
if (flags & ZFS_ITER_RECURSE) {
|
|
|
|
ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
|
2010-05-29 00:45:14 +04:00
|
|
|
NULL, NULL, 0, inherit_recurse_cb, &cb);
|
2008-12-03 23:09:06 +03:00
|
|
|
} else {
|
|
|
|
ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
|
2010-05-29 00:45:14 +04:00
|
|
|
NULL, NULL, 0, inherit_cb, &cb);
|
2008-12-03 23:09:06 +03:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct upgrade_cbdata {
|
|
|
|
uint64_t cb_numupgraded;
|
|
|
|
uint64_t cb_numsamegraded;
|
|
|
|
uint64_t cb_numfailed;
|
|
|
|
uint64_t cb_version;
|
|
|
|
boolean_t cb_newer;
|
|
|
|
boolean_t cb_foundone;
|
2016-06-16 00:28:36 +03:00
|
|
|
char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 23:01:55 +03:00
|
|
|
} upgrade_cbdata_t;
|
|
|
|
|
|
|
|
static int
|
|
|
|
same_pool(zfs_handle_t *zhp, const char *name)
|
|
|
|
{
|
|
|
|
int len1 = strcspn(name, "/@");
|
|
|
|
const char *zhname = zfs_get_name(zhp);
|
|
|
|
int len2 = strcspn(zhname, "/@");
|
|
|
|
|
|
|
|
if (len1 != len2)
|
|
|
|
return (B_FALSE);
|
|
|
|
return (strncmp(name, zhname, len1) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
upgrade_list_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
upgrade_cbdata_t *cb = data;
|
|
|
|
int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
|
|
|
|
|
|
|
|
/* list if it's old/new */
|
|
|
|
if ((!cb->cb_newer && version < ZPL_VERSION) ||
|
|
|
|
(cb->cb_newer && version > ZPL_VERSION)) {
|
|
|
|
char *str;
|
|
|
|
if (cb->cb_newer) {
|
|
|
|
str = gettext("The following filesystems are "
|
|
|
|
"formatted using a newer software version and\n"
|
|
|
|
"cannot be accessed on the current system.\n\n");
|
|
|
|
} else {
|
|
|
|
str = gettext("The following filesystems are "
|
|
|
|
"out of date, and can be upgraded. After being\n"
|
|
|
|
"upgraded, these filesystems (and any 'zfs send' "
|
|
|
|
"streams generated from\n"
|
|
|
|
"subsequent snapshots) will no longer be "
|
|
|
|
"accessible by older software versions.\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cb->cb_foundone) {
|
|
|
|
(void) puts(str);
|
|
|
|
(void) printf(gettext("VER FILESYSTEM\n"));
|
|
|
|
(void) printf(gettext("--- ------------\n"));
|
|
|
|
cb->cb_foundone = B_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) printf("%2u %s\n", version, zfs_get_name(zhp));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
upgrade_set_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
upgrade_cbdata_t *cb = data;
|
|
|
|
int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
|
2010-05-29 00:45:14 +04:00
|
|
|
int needed_spa_version;
|
|
|
|
int spa_version;
|
|
|
|
|
|
|
|
if (zfs_spa_version(zhp, &spa_version) < 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
needed_spa_version = zfs_spa_version_map(cb->cb_version);
|
|
|
|
|
|
|
|
if (needed_spa_version < 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
if (spa_version < needed_spa_version) {
|
|
|
|
/* can't upgrade */
|
|
|
|
(void) printf(gettext("%s: can not be "
|
|
|
|
"upgraded; the pool version needs to first "
|
|
|
|
"be upgraded\nto version %d\n\n"),
|
|
|
|
zfs_get_name(zhp), needed_spa_version);
|
|
|
|
cb->cb_numfailed++;
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* upgrade */
|
|
|
|
if (version < cb->cb_version) {
|
|
|
|
char verstr[16];
|
|
|
|
(void) snprintf(verstr, sizeof (verstr),
|
2010-08-26 20:52:39 +04:00
|
|
|
"%llu", (u_longlong_t)cb->cb_version);
|
2008-11-20 23:01:55 +03:00
|
|
|
if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
|
|
|
|
/*
|
|
|
|
* If they did "zfs upgrade -a", then we could
|
|
|
|
* be doing ioctls to different pools. We need
|
2013-08-28 15:45:09 +04:00
|
|
|
* to log this history once to each pool, and bypass
|
|
|
|
* the normal history logging that happens in main().
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
2013-08-28 15:45:09 +04:00
|
|
|
(void) zpool_log_history(g_zfs, history_str);
|
|
|
|
log_history = B_FALSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
if (zfs_prop_set(zhp, "version", verstr) == 0)
|
|
|
|
cb->cb_numupgraded++;
|
|
|
|
else
|
|
|
|
cb->cb_numfailed++;
|
|
|
|
(void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
|
|
|
|
} else if (version > cb->cb_version) {
|
|
|
|
/* can't downgrade */
|
|
|
|
(void) printf(gettext("%s: can not be downgraded; "
|
|
|
|
"it is already at version %u\n"),
|
|
|
|
zfs_get_name(zhp), version);
|
|
|
|
cb->cb_numfailed++;
|
|
|
|
} else {
|
|
|
|
cb->cb_numsamegraded++;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* zfs upgrade
|
|
|
|
* zfs upgrade -v
|
|
|
|
* zfs upgrade [-r] [-V <version>] <-a | filesystem>
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_upgrade(int argc, char **argv)
|
|
|
|
{
|
|
|
|
boolean_t all = B_FALSE;
|
|
|
|
boolean_t showversions = B_FALSE;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
upgrade_cbdata_t cb = { 0 };
|
2019-05-28 21:14:58 +03:00
|
|
|
int c;
|
2008-12-03 23:09:06 +03:00
|
|
|
int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
|
|
|
while ((c = getopt(argc, argv, "rvV:a")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
2008-12-03 23:09:06 +03:00
|
|
|
flags |= ZFS_ITER_RECURSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
showversions = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
|
|
|
|
optarg, &cb.cb_version) != 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid version %s\n"), optarg);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
all = B_TRUE;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
2008-12-03 23:09:06 +03:00
|
|
|
if (showversions && (flags & ZFS_ITER_RECURSE || all ||
|
|
|
|
cb.cb_version || argc))
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
if ((all || argc) && (showversions))
|
|
|
|
usage(B_FALSE);
|
|
|
|
if (all && argc)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
|
|
|
if (showversions) {
|
|
|
|
/* Show info on available versions. */
|
|
|
|
(void) printf(gettext("The following filesystem versions are "
|
|
|
|
"supported:\n\n"));
|
|
|
|
(void) printf(gettext("VER DESCRIPTION\n"));
|
|
|
|
(void) printf("--- -----------------------------------------"
|
|
|
|
"---------------\n");
|
|
|
|
(void) printf(gettext(" 1 Initial ZFS filesystem version\n"));
|
|
|
|
(void) printf(gettext(" 2 Enhanced directory entries\n"));
|
2011-11-17 22:14:36 +04:00
|
|
|
(void) printf(gettext(" 3 Case insensitive and filesystem "
|
|
|
|
"user identifier (FUID)\n"));
|
2009-07-03 02:44:48 +04:00
|
|
|
(void) printf(gettext(" 4 userquota, groupquota "
|
|
|
|
"properties\n"));
|
2010-05-29 00:45:14 +04:00
|
|
|
(void) printf(gettext(" 5 System attributes\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) printf(gettext("\nFor more information on a particular "
|
2010-05-29 00:45:14 +04:00
|
|
|
"version, including supported releases,\n"));
|
|
|
|
(void) printf("see the ZFS Administration Guide.\n\n");
|
2008-11-20 23:01:55 +03:00
|
|
|
ret = 0;
|
|
|
|
} else if (argc || all) {
|
|
|
|
/* Upgrade filesystems */
|
|
|
|
if (cb.cb_version == 0)
|
|
|
|
cb.cb_version = ZPL_VERSION;
|
2008-12-03 23:09:06 +03:00
|
|
|
ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
|
2009-07-03 02:44:48 +04:00
|
|
|
NULL, NULL, 0, upgrade_set_callback, &cb);
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) printf(gettext("%llu filesystems upgraded\n"),
|
2010-08-26 20:52:39 +04:00
|
|
|
(u_longlong_t)cb.cb_numupgraded);
|
2008-11-20 23:01:55 +03:00
|
|
|
if (cb.cb_numsamegraded) {
|
|
|
|
(void) printf(gettext("%llu filesystems already at "
|
|
|
|
"this version\n"),
|
2010-08-26 20:52:39 +04:00
|
|
|
(u_longlong_t)cb.cb_numsamegraded);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
if (cb.cb_numfailed != 0)
|
|
|
|
ret = 1;
|
|
|
|
} else {
|
2017-01-03 20:31:18 +03:00
|
|
|
/* List old-version filesystems */
|
2008-11-20 23:01:55 +03:00
|
|
|
boolean_t found;
|
|
|
|
(void) printf(gettext("This system is currently running "
|
|
|
|
"ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
flags |= ZFS_ITER_RECURSE;
|
|
|
|
ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
|
2009-07-03 02:44:48 +04:00
|
|
|
NULL, NULL, 0, upgrade_list_callback, &cb);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
found = cb.cb_foundone;
|
|
|
|
cb.cb_foundone = B_FALSE;
|
|
|
|
cb.cb_newer = B_TRUE;
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
|
2009-07-03 02:44:48 +04:00
|
|
|
NULL, NULL, 0, upgrade_list_callback, &cb);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (!cb.cb_foundone && !found) {
|
|
|
|
(void) printf(gettext("All filesystems are "
|
|
|
|
"formatted with the current version.\n"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/*
|
|
|
|
* zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
|
|
|
|
* [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
|
|
|
|
* zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
|
|
|
|
* [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
|
2018-02-14 01:54:54 +03:00
|
|
|
* zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...]
|
|
|
|
* [-S field [-S field]...] filesystem | snapshot
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
*
|
|
|
|
* -H Scripted mode; elide headers and separate columns by tabs.
|
|
|
|
* -i Translate SID to POSIX ID.
|
|
|
|
* -n Print numeric ID instead of user/group name.
|
|
|
|
* -o Control which fields to display.
|
2013-11-19 19:41:37 +04:00
|
|
|
* -p Use exact (parsable) numeric output.
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
* -s Specify sort columns, descending order.
|
|
|
|
* -S Specify sort columns, ascending order.
|
|
|
|
* -t Control which object types to display.
|
|
|
|
*
|
|
|
|
* Displays space consumed by, and quotas on, each user in the specified
|
|
|
|
* filesystem or snapshot.
|
|
|
|
*/
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* us_field_types, us_field_hdr and us_field_names should be kept in sync */
|
|
|
|
enum us_field_types {
|
|
|
|
USFIELD_TYPE,
|
|
|
|
USFIELD_NAME,
|
|
|
|
USFIELD_USED,
|
2016-10-04 21:46:10 +03:00
|
|
|
USFIELD_QUOTA,
|
|
|
|
USFIELD_OBJUSED,
|
|
|
|
USFIELD_OBJQUOTA
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
};
|
2016-10-04 21:46:10 +03:00
|
|
|
static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA",
|
|
|
|
"OBJUSED", "OBJQUOTA" };
|
|
|
|
static char *us_field_names[] = { "type", "name", "used", "quota",
|
|
|
|
"objused", "objquota" };
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *))
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
#define USTYPE_PSX_GRP (1 << 0)
|
|
|
|
#define USTYPE_PSX_USR (1 << 1)
|
|
|
|
#define USTYPE_SMB_GRP (1 << 2)
|
|
|
|
#define USTYPE_SMB_USR (1 << 3)
|
2018-02-14 01:54:54 +03:00
|
|
|
#define USTYPE_PROJ (1 << 4)
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
#define USTYPE_ALL \
|
2018-02-14 01:54:54 +03:00
|
|
|
(USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \
|
|
|
|
USTYPE_PROJ)
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
static int us_type_bits[] = {
|
|
|
|
USTYPE_PSX_GRP,
|
|
|
|
USTYPE_PSX_USR,
|
|
|
|
USTYPE_SMB_GRP,
|
|
|
|
USTYPE_SMB_USR,
|
|
|
|
USTYPE_ALL
|
|
|
|
};
|
2013-12-19 03:09:45 +04:00
|
|
|
static char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup",
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
"smbuser", "all" };
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
typedef struct us_node {
|
|
|
|
nvlist_t *usn_nvl;
|
|
|
|
uu_avl_node_t usn_avlnode;
|
|
|
|
uu_list_node_t usn_listnode;
|
|
|
|
} us_node_t;
|
|
|
|
|
|
|
|
typedef struct us_cbdata {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
nvlist_t **cb_nvlp;
|
|
|
|
uu_avl_pool_t *cb_avl_pool;
|
|
|
|
uu_avl_t *cb_avl;
|
|
|
|
boolean_t cb_numname;
|
|
|
|
boolean_t cb_nicenum;
|
|
|
|
boolean_t cb_sid2posix;
|
|
|
|
zfs_userquota_prop_t cb_prop;
|
|
|
|
zfs_sort_column_t *cb_sortcol;
|
|
|
|
size_t cb_width[USFIELD_LAST];
|
2011-07-27 02:44:36 +04:00
|
|
|
} us_cbdata_t;
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
static boolean_t us_populated = B_FALSE;
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
typedef struct {
|
|
|
|
zfs_sort_column_t *si_sortcol;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
boolean_t si_numname;
|
2011-07-27 02:44:36 +04:00
|
|
|
} us_sort_info_t;
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
static int
|
|
|
|
us_field_index(char *field)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < USFIELD_LAST; i++) {
|
|
|
|
if (strcmp(field, us_field_names[i]) == 0)
|
|
|
|
return (i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
static int
|
|
|
|
us_compare(const void *larg, const void *rarg, void *unused)
|
|
|
|
{
|
|
|
|
const us_node_t *l = larg;
|
|
|
|
const us_node_t *r = rarg;
|
|
|
|
us_sort_info_t *si = (us_sort_info_t *)unused;
|
|
|
|
zfs_sort_column_t *sortcol = si->si_sortcol;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
boolean_t numname = si->si_numname;
|
2011-07-27 02:44:36 +04:00
|
|
|
nvlist_t *lnvl = l->usn_nvl;
|
|
|
|
nvlist_t *rnvl = r->usn_nvl;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
int rc = 0;
|
|
|
|
boolean_t lvb, rvb;
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
for (; sortcol != NULL; sortcol = sortcol->sc_next) {
|
|
|
|
char *lvstr = "";
|
|
|
|
char *rvstr = "";
|
|
|
|
uint32_t lv32 = 0;
|
|
|
|
uint32_t rv32 = 0;
|
|
|
|
uint64_t lv64 = 0;
|
|
|
|
uint64_t rv64 = 0;
|
|
|
|
zfs_prop_t prop = sortcol->sc_prop;
|
|
|
|
const char *propname = NULL;
|
|
|
|
boolean_t reverse = sortcol->sc_reverse;
|
|
|
|
|
|
|
|
switch (prop) {
|
|
|
|
case ZFS_PROP_TYPE:
|
|
|
|
propname = "type";
|
|
|
|
(void) nvlist_lookup_uint32(lnvl, propname, &lv32);
|
|
|
|
(void) nvlist_lookup_uint32(rnvl, propname, &rv32);
|
|
|
|
if (rv32 != lv32)
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
rc = (rv32 < lv32) ? 1 : -1;
|
2011-07-27 02:44:36 +04:00
|
|
|
break;
|
|
|
|
case ZFS_PROP_NAME:
|
|
|
|
propname = "name";
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (numname) {
|
2016-03-28 01:28:32 +03:00
|
|
|
compare_nums:
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
(void) nvlist_lookup_uint64(lnvl, propname,
|
|
|
|
&lv64);
|
|
|
|
(void) nvlist_lookup_uint64(rnvl, propname,
|
|
|
|
&rv64);
|
|
|
|
if (rv64 != lv64)
|
|
|
|
rc = (rv64 < lv64) ? 1 : -1;
|
2011-07-27 02:44:36 +04:00
|
|
|
} else {
|
2016-03-28 01:28:32 +03:00
|
|
|
if ((nvlist_lookup_string(lnvl, propname,
|
2016-12-12 21:46:26 +03:00
|
|
|
&lvstr) == ENOENT) ||
|
2016-03-28 01:28:32 +03:00
|
|
|
(nvlist_lookup_string(rnvl, propname,
|
2016-12-12 21:46:26 +03:00
|
|
|
&rvstr) == ENOENT)) {
|
2016-03-28 01:28:32 +03:00
|
|
|
goto compare_nums;
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
rc = strcmp(lvstr, rvstr);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ZFS_PROP_USED:
|
|
|
|
case ZFS_PROP_QUOTA:
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (!us_populated)
|
|
|
|
break;
|
|
|
|
if (prop == ZFS_PROP_USED)
|
2011-07-27 02:44:36 +04:00
|
|
|
propname = "used";
|
|
|
|
else
|
|
|
|
propname = "quota";
|
|
|
|
(void) nvlist_lookup_uint64(lnvl, propname, &lv64);
|
|
|
|
(void) nvlist_lookup_uint64(rnvl, propname, &rv64);
|
|
|
|
if (rv64 != lv64)
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
rc = (rv64 < lv64) ? 1 : -1;
|
|
|
|
break;
|
2017-01-18 01:45:02 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (rc != 0) {
|
2011-07-27 02:44:36 +04:00
|
|
|
if (rc < 0)
|
|
|
|
return (reverse ? 1 : -1);
|
|
|
|
else
|
|
|
|
return (reverse ? -1 : 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/*
|
|
|
|
* If entries still seem to be the same, check if they are of the same
|
|
|
|
* type (smbentity is added only if we are doing SID to POSIX ID
|
|
|
|
* translation where we can have duplicate type/name combinations).
|
|
|
|
*/
|
|
|
|
if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
|
|
|
|
nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
|
|
|
|
lvb != rvb)
|
|
|
|
return (lvb < rvb ? -1 : 1);
|
|
|
|
|
|
|
|
return (0);
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
2016-10-04 21:46:10 +03:00
|
|
|
static boolean_t
|
|
|
|
zfs_prop_is_user(unsigned p)
|
|
|
|
{
|
|
|
|
return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||
|
|
|
|
p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA);
|
|
|
|
}
|
|
|
|
|
|
|
|
static boolean_t
|
|
|
|
zfs_prop_is_group(unsigned p)
|
|
|
|
{
|
|
|
|
return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||
|
|
|
|
p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA);
|
|
|
|
}
|
|
|
|
|
2018-02-14 01:54:54 +03:00
|
|
|
static boolean_t
|
|
|
|
zfs_prop_is_project(unsigned p)
|
|
|
|
{
|
|
|
|
return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA ||
|
|
|
|
p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA);
|
|
|
|
}
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
static inline const char *
|
|
|
|
us_type2str(unsigned field_type)
|
|
|
|
{
|
|
|
|
switch (field_type) {
|
|
|
|
case USTYPE_PSX_USR:
|
|
|
|
return ("POSIX User");
|
|
|
|
case USTYPE_PSX_GRP:
|
|
|
|
return ("POSIX Group");
|
|
|
|
case USTYPE_SMB_USR:
|
|
|
|
return ("SMB User");
|
|
|
|
case USTYPE_SMB_GRP:
|
|
|
|
return ("SMB Group");
|
2018-02-14 01:54:54 +03:00
|
|
|
case USTYPE_PROJ:
|
|
|
|
return ("Project");
|
2011-07-27 02:44:36 +04:00
|
|
|
default:
|
|
|
|
return ("Undefined");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-03 02:44:48 +04:00
|
|
|
static int
|
|
|
|
userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
|
|
|
|
{
|
2011-07-27 02:44:36 +04:00
|
|
|
us_cbdata_t *cb = (us_cbdata_t *)arg;
|
|
|
|
zfs_userquota_prop_t prop = cb->cb_prop;
|
2009-07-03 02:44:48 +04:00
|
|
|
char *name = NULL;
|
2011-07-27 02:44:36 +04:00
|
|
|
char *propname;
|
2009-07-03 02:44:48 +04:00
|
|
|
char sizebuf[32];
|
2011-07-27 02:44:36 +04:00
|
|
|
us_node_t *node;
|
|
|
|
uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
|
|
|
|
uu_avl_t *avl = cb->cb_avl;
|
|
|
|
uu_avl_index_t idx;
|
|
|
|
nvlist_t *props;
|
|
|
|
us_node_t *n;
|
|
|
|
zfs_sort_column_t *sortcol = cb->cb_sortcol;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
unsigned type = 0;
|
2011-07-27 02:44:36 +04:00
|
|
|
const char *typestr;
|
|
|
|
size_t namelen;
|
|
|
|
size_t typelen;
|
|
|
|
size_t sizelen;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
int typeidx, nameidx, sizeidx;
|
2011-07-27 02:44:36 +04:00
|
|
|
us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
boolean_t smbentity = B_FALSE;
|
2009-07-03 02:44:48 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
|
|
|
node = safe_malloc(sizeof (us_node_t));
|
|
|
|
uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
|
|
|
|
node->usn_nvl = props;
|
|
|
|
|
|
|
|
if (domain != NULL && domain[0] != '\0') {
|
2011-07-27 02:44:36 +04:00
|
|
|
#ifdef HAVE_IDMAP
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* SMB */
|
2016-06-16 00:28:36 +03:00
|
|
|
char sid[MAXNAMELEN + 32];
|
2011-07-27 02:44:36 +04:00
|
|
|
uid_t id;
|
|
|
|
uint64_t classes;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
int err;
|
2011-07-27 02:44:36 +04:00
|
|
|
directory_error_t e;
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
smbentity = B_TRUE;
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
(void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
|
|
|
|
type = USTYPE_SMB_GRP;
|
|
|
|
err = sid_to_id(sid, B_FALSE, &id);
|
|
|
|
} else {
|
|
|
|
type = USTYPE_SMB_USR;
|
|
|
|
err = sid_to_id(sid, B_TRUE, &id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
rid = id;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (!cb->cb_sid2posix) {
|
|
|
|
e = directory_name_from_sid(NULL, sid, &name,
|
|
|
|
&classes);
|
2013-07-17 00:54:29 +04:00
|
|
|
if (e != NULL)
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
directory_error_free(e);
|
|
|
|
if (name == NULL)
|
|
|
|
name = sid;
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
nvlist_free(props);
|
|
|
|
free(node);
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
return (-1);
|
|
|
|
#endif /* HAVE_IDMAP */
|
2009-07-03 02:44:48 +04:00
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
|
|
|
|
/* POSIX or -i */
|
2016-10-04 21:46:10 +03:00
|
|
|
if (zfs_prop_is_group(prop)) {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
type = USTYPE_PSX_GRP;
|
|
|
|
if (!cb->cb_numname) {
|
|
|
|
struct group *g;
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if ((g = getgrgid(rid)) != NULL)
|
|
|
|
name = g->gr_name;
|
|
|
|
}
|
2018-02-14 01:54:54 +03:00
|
|
|
} else if (zfs_prop_is_user(prop)) {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
type = USTYPE_PSX_USR;
|
|
|
|
if (!cb->cb_numname) {
|
|
|
|
struct passwd *p;
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if ((p = getpwuid(rid)) != NULL)
|
|
|
|
name = p->pw_name;
|
|
|
|
}
|
2018-02-14 01:54:54 +03:00
|
|
|
} else {
|
|
|
|
type = USTYPE_PROJ;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/*
|
|
|
|
* Make sure that the type/name combination is unique when doing
|
|
|
|
* SID to POSIX ID translation (hence changing the type from SMB to
|
|
|
|
* POSIX).
|
|
|
|
*/
|
|
|
|
if (cb->cb_sid2posix &&
|
|
|
|
nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
/* Calculate/update width of TYPE field */
|
|
|
|
typestr = us_type2str(type);
|
|
|
|
typelen = strlen(gettext(typestr));
|
|
|
|
typeidx = us_field_index("type");
|
|
|
|
if (typelen > cb->cb_width[typeidx])
|
|
|
|
cb->cb_width[typeidx] = typelen;
|
2011-07-27 02:44:36 +04:00
|
|
|
if (nvlist_add_uint32(props, "type", type) != 0)
|
|
|
|
nomem();
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* Calculate/update width of NAME field */
|
|
|
|
if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
|
|
|
|
if (nvlist_add_uint64(props, "name", rid) != 0)
|
2011-07-27 02:44:36 +04:00
|
|
|
nomem();
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
namelen = snprintf(NULL, 0, "%u", rid);
|
2011-07-27 02:44:36 +04:00
|
|
|
} else {
|
|
|
|
if (nvlist_add_string(props, "name", name) != 0)
|
|
|
|
nomem();
|
|
|
|
namelen = strlen(name);
|
|
|
|
}
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
nameidx = us_field_index("name");
|
2016-12-16 20:11:17 +03:00
|
|
|
if (nameidx >= 0 && namelen > cb->cb_width[nameidx])
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
cb->cb_width[nameidx] = namelen;
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/*
|
|
|
|
* Check if this type/name combination is in the list and update it;
|
|
|
|
* otherwise add new node to the list.
|
|
|
|
*/
|
|
|
|
if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
|
2011-07-27 02:44:36 +04:00
|
|
|
uu_avl_insert(avl, node, idx);
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
} else {
|
2011-07-27 02:44:36 +04:00
|
|
|
nvlist_free(props);
|
|
|
|
free(node);
|
|
|
|
node = n;
|
|
|
|
props = node->usn_nvl;
|
2009-07-03 02:44:48 +04:00
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* Calculate/update width of USED/QUOTA fields */
|
2017-05-02 23:43:53 +03:00
|
|
|
if (cb->cb_nicenum) {
|
|
|
|
if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
|
2018-02-14 01:54:54 +03:00
|
|
|
prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
|
|
|
|
prop == ZFS_PROP_PROJECTUSED ||
|
|
|
|
prop == ZFS_PROP_PROJECTQUOTA) {
|
2017-05-02 23:43:53 +03:00
|
|
|
zfs_nicebytes(space, sizebuf, sizeof (sizebuf));
|
|
|
|
} else {
|
|
|
|
zfs_nicenum(space, sizebuf, sizeof (sizebuf));
|
|
|
|
}
|
|
|
|
} else {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
(void) snprintf(sizebuf, sizeof (sizebuf), "%llu",
|
|
|
|
(u_longlong_t)space);
|
2017-05-02 23:43:53 +03:00
|
|
|
}
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
sizelen = strlen(sizebuf);
|
2018-02-14 01:54:54 +03:00
|
|
|
if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
|
|
|
|
prop == ZFS_PROP_PROJECTUSED) {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
propname = "used";
|
|
|
|
if (!nvlist_exists(props, "quota"))
|
|
|
|
(void) nvlist_add_uint64(props, "quota", 0);
|
2018-02-14 01:54:54 +03:00
|
|
|
} else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
|
|
|
|
prop == ZFS_PROP_PROJECTQUOTA) {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
propname = "quota";
|
|
|
|
if (!nvlist_exists(props, "used"))
|
|
|
|
(void) nvlist_add_uint64(props, "used", 0);
|
2016-10-04 21:46:10 +03:00
|
|
|
} else if (prop == ZFS_PROP_USEROBJUSED ||
|
2018-02-14 01:54:54 +03:00
|
|
|
prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) {
|
2016-10-04 21:46:10 +03:00
|
|
|
propname = "objused";
|
|
|
|
if (!nvlist_exists(props, "objquota"))
|
|
|
|
(void) nvlist_add_uint64(props, "objquota", 0);
|
|
|
|
} else if (prop == ZFS_PROP_USEROBJQUOTA ||
|
2018-02-14 01:54:54 +03:00
|
|
|
prop == ZFS_PROP_GROUPOBJQUOTA ||
|
|
|
|
prop == ZFS_PROP_PROJECTOBJQUOTA) {
|
2016-10-04 21:46:10 +03:00
|
|
|
propname = "objquota";
|
|
|
|
if (!nvlist_exists(props, "objused"))
|
|
|
|
(void) nvlist_add_uint64(props, "objused", 0);
|
|
|
|
} else {
|
|
|
|
return (-1);
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
}
|
|
|
|
sizeidx = us_field_index(propname);
|
2016-12-16 20:11:17 +03:00
|
|
|
if (sizeidx >= 0 && sizelen > cb->cb_width[sizeidx])
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
cb->cb_width[sizeidx] = sizelen;
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
if (nvlist_add_uint64(props, propname, space) != 0)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
|
|
|
|
size_t *width, us_node_t *node)
|
2011-07-27 02:44:36 +04:00
|
|
|
{
|
|
|
|
nvlist_t *nvl = node->usn_nvl;
|
2016-06-16 00:28:36 +03:00
|
|
|
char valstr[MAXNAMELEN];
|
2011-07-27 02:44:36 +04:00
|
|
|
boolean_t first = B_TRUE;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
int cfield = 0;
|
|
|
|
int field;
|
|
|
|
uint32_t ustype;
|
|
|
|
|
|
|
|
/* Check type */
|
|
|
|
(void) nvlist_lookup_uint32(nvl, "type", &ustype);
|
|
|
|
if (!(ustype & types))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while ((field = fields[cfield]) != USFIELD_LAST) {
|
|
|
|
nvpair_t *nvp = NULL;
|
|
|
|
data_type_t type;
|
|
|
|
uint32_t val32;
|
|
|
|
uint64_t val64;
|
2016-10-04 21:46:10 +03:00
|
|
|
char *strval = "-";
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
|
|
|
|
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
|
|
|
|
if (strcmp(nvpair_name(nvp),
|
|
|
|
us_field_names[field]) == 0)
|
2011-07-27 02:44:36 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-04 21:46:10 +03:00
|
|
|
type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp);
|
2011-07-27 02:44:36 +04:00
|
|
|
switch (type) {
|
|
|
|
case DATA_TYPE_UINT32:
|
|
|
|
(void) nvpair_value_uint32(nvp, &val32);
|
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT64:
|
|
|
|
(void) nvpair_value_uint64(nvp, &val64);
|
|
|
|
break;
|
|
|
|
case DATA_TYPE_STRING:
|
|
|
|
(void) nvpair_value_string(nvp, &strval);
|
|
|
|
break;
|
2016-10-04 21:46:10 +03:00
|
|
|
case DATA_TYPE_UNKNOWN:
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
default:
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
(void) fprintf(stderr, "invalid data type\n");
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (field) {
|
|
|
|
case USFIELD_TYPE:
|
2016-10-04 21:46:10 +03:00
|
|
|
if (type == DATA_TYPE_UINT32)
|
|
|
|
strval = (char *)us_type2str(val32);
|
2011-07-27 02:44:36 +04:00
|
|
|
break;
|
|
|
|
case USFIELD_NAME:
|
|
|
|
if (type == DATA_TYPE_UINT64) {
|
|
|
|
(void) sprintf(valstr, "%llu",
|
2016-12-12 21:46:26 +03:00
|
|
|
(u_longlong_t)val64);
|
2011-07-27 02:44:36 +04:00
|
|
|
strval = valstr;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USFIELD_USED:
|
|
|
|
case USFIELD_QUOTA:
|
2017-05-02 23:43:53 +03:00
|
|
|
if (type == DATA_TYPE_UINT64) {
|
|
|
|
if (parsable) {
|
|
|
|
(void) sprintf(valstr, "%llu",
|
|
|
|
(u_longlong_t)val64);
|
|
|
|
strval = valstr;
|
|
|
|
} else if (field == USFIELD_QUOTA &&
|
|
|
|
val64 == 0) {
|
|
|
|
strval = "none";
|
|
|
|
} else {
|
|
|
|
zfs_nicebytes(val64, valstr,
|
|
|
|
sizeof (valstr));
|
|
|
|
strval = valstr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2016-10-04 21:46:10 +03:00
|
|
|
case USFIELD_OBJUSED:
|
|
|
|
case USFIELD_OBJQUOTA:
|
2011-07-27 02:44:36 +04:00
|
|
|
if (type == DATA_TYPE_UINT64) {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (parsable) {
|
2011-07-27 02:44:36 +04:00
|
|
|
(void) sprintf(valstr, "%llu",
|
2016-12-12 21:46:26 +03:00
|
|
|
(u_longlong_t)val64);
|
2017-05-02 23:43:53 +03:00
|
|
|
strval = valstr;
|
|
|
|
} else if (field == USFIELD_OBJQUOTA &&
|
|
|
|
val64 == 0) {
|
|
|
|
strval = "none";
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
} else {
|
2011-07-27 02:44:36 +04:00
|
|
|
zfs_nicenum(val64, valstr,
|
|
|
|
sizeof (valstr));
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
strval = valstr;
|
2017-05-02 23:43:53 +03:00
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (!first) {
|
|
|
|
if (scripted)
|
|
|
|
(void) printf("\t");
|
2011-07-27 02:44:36 +04:00
|
|
|
else
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
(void) printf(" ");
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (scripted)
|
|
|
|
(void) printf("%s", strval);
|
|
|
|
else if (field == USFIELD_TYPE || field == USFIELD_NAME)
|
2016-12-12 21:46:26 +03:00
|
|
|
(void) printf("%-*s", (int)width[field], strval);
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
else
|
2016-12-12 21:46:26 +03:00
|
|
|
(void) printf("%*s", (int)width[field], strval);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
first = B_FALSE;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
cfield++;
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
|
|
|
|
size_t *width, boolean_t rmnode, uu_avl_t *avl)
|
2011-07-27 02:44:36 +04:00
|
|
|
{
|
|
|
|
us_node_t *node;
|
|
|
|
const char *col;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
int cfield = 0;
|
|
|
|
int field;
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
if (!scripted) {
|
|
|
|
boolean_t first = B_TRUE;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
|
|
|
|
while ((field = fields[cfield]) != USFIELD_LAST) {
|
|
|
|
col = gettext(us_field_hdr[field]);
|
|
|
|
if (field == USFIELD_TYPE || field == USFIELD_NAME) {
|
|
|
|
(void) printf(first ? "%-*s" : " %-*s",
|
2016-12-12 21:46:26 +03:00
|
|
|
(int)width[field], col);
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
} else {
|
|
|
|
(void) printf(first ? "%*s" : " %*s",
|
2016-12-12 21:46:26 +03:00
|
|
|
(int)width[field], col);
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
first = B_FALSE;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
cfield++;
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
(void) printf("\n");
|
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
|
|
|
|
print_us_node(scripted, parsable, fields, types, width, node);
|
2011-07-27 02:44:36 +04:00
|
|
|
if (rmnode)
|
|
|
|
nvlist_free(node->usn_nvl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-03 02:44:48 +04:00
|
|
|
static int
|
|
|
|
zfs_do_userspace(int argc, char **argv)
|
|
|
|
{
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
zfs_userquota_prop_t p;
|
2011-07-27 02:44:36 +04:00
|
|
|
uu_avl_pool_t *avl_pool;
|
|
|
|
uu_avl_t *avl_tree;
|
|
|
|
uu_avl_walk_t *walk;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
char *delim;
|
2016-10-04 21:46:10 +03:00
|
|
|
char deffields[] = "type,name,used,quota,objused,objquota";
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
char *ofield = NULL;
|
|
|
|
char *tfield = NULL;
|
|
|
|
int cfield = 0;
|
|
|
|
int fields[256];
|
|
|
|
int i;
|
2011-07-27 02:44:36 +04:00
|
|
|
boolean_t scripted = B_FALSE;
|
|
|
|
boolean_t prtnum = B_FALSE;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
boolean_t parsable = B_FALSE;
|
2011-07-27 02:44:36 +04:00
|
|
|
boolean_t sid2posix = B_FALSE;
|
2013-07-17 00:54:29 +04:00
|
|
|
int ret = 0;
|
2011-07-27 02:44:36 +04:00
|
|
|
int c;
|
|
|
|
zfs_sort_column_t *sortcol = NULL;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
|
2011-07-27 02:44:36 +04:00
|
|
|
us_cbdata_t cb;
|
|
|
|
us_node_t *node;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
us_node_t *rmnode;
|
|
|
|
uu_list_pool_t *listpool;
|
|
|
|
uu_list_t *list;
|
|
|
|
uu_avl_index_t idx = 0;
|
|
|
|
uu_list_index_t idx2 = 0;
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
2018-02-14 01:54:54 +03:00
|
|
|
if (strcmp(argv[0], "groupspace") == 0) {
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* Toggle default group types */
|
2011-07-27 02:44:36 +04:00
|
|
|
types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
|
2018-02-14 01:54:54 +03:00
|
|
|
} else if (strcmp(argv[0], "projectspace") == 0) {
|
|
|
|
types = USTYPE_PROJ;
|
|
|
|
prtnum = B_TRUE;
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'n':
|
2018-02-14 01:54:54 +03:00
|
|
|
if (types == USTYPE_PROJ) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option 'n'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
prtnum = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
scripted = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'p':
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
parsable = B_TRUE;
|
2011-07-27 02:44:36 +04:00
|
|
|
break;
|
|
|
|
case 'o':
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
ofield = optarg;
|
2011-07-27 02:44:36 +04:00
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
case 'S':
|
|
|
|
if (zfs_add_sort_column(&sortcol, optarg,
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
c == 's' ? B_FALSE : B_TRUE) != 0) {
|
2011-07-27 02:44:36 +04:00
|
|
|
(void) fprintf(stderr,
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
gettext("invalid field '%s'\n"), optarg);
|
2011-07-27 02:44:36 +04:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 't':
|
2018-02-14 01:54:54 +03:00
|
|
|
if (types == USTYPE_PROJ) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option 't'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
tfield = optarg;
|
2011-07-27 02:44:36 +04:00
|
|
|
break;
|
|
|
|
case 'i':
|
2018-02-14 01:54:54 +03:00
|
|
|
if (types == USTYPE_PROJ) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option 'i'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
sid2posix = B_TRUE;
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing argument for "
|
|
|
|
"'%c' option\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing dataset name\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* Use default output fields if not specified using -o */
|
|
|
|
if (ofield == NULL)
|
|
|
|
ofield = deffields;
|
|
|
|
do {
|
|
|
|
if ((delim = strchr(ofield, ',')) != NULL)
|
|
|
|
*delim = '\0';
|
|
|
|
if ((fields[cfield++] = us_field_index(ofield)) == -1) {
|
|
|
|
(void) fprintf(stderr, gettext("invalid type '%s' "
|
|
|
|
"for -o option\n"), ofield);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if (delim != NULL)
|
|
|
|
ofield = delim + 1;
|
|
|
|
} while (delim != NULL);
|
|
|
|
fields[cfield] = USFIELD_LAST;
|
|
|
|
|
|
|
|
/* Override output types (-t option) */
|
|
|
|
if (tfield != NULL) {
|
|
|
|
types = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
boolean_t found = B_FALSE;
|
|
|
|
|
|
|
|
if ((delim = strchr(tfield, ',')) != NULL)
|
|
|
|
*delim = '\0';
|
|
|
|
for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
|
|
|
|
i++) {
|
|
|
|
if (strcmp(tfield, us_type_names[i]) == 0) {
|
|
|
|
found = B_TRUE;
|
|
|
|
types |= us_type_bits[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
(void) fprintf(stderr, gettext("invalid type "
|
|
|
|
"'%s' for -t option\n"), tfield);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if (delim != NULL)
|
|
|
|
tfield = delim + 1;
|
|
|
|
} while (delim != NULL);
|
|
|
|
}
|
2009-07-03 02:44:48 +04:00
|
|
|
|
2019-01-25 20:47:52 +03:00
|
|
|
if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM |
|
|
|
|
ZFS_TYPE_SNAPSHOT)) == NULL)
|
2009-07-03 02:44:48 +04:00
|
|
|
return (1);
|
2019-01-25 20:47:52 +03:00
|
|
|
if (zhp->zfs_head_type != ZFS_TYPE_FILESYSTEM) {
|
|
|
|
(void) fprintf(stderr, gettext("operation is only applicable "
|
|
|
|
"to filesystems and their snapshots\n"));
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (1);
|
|
|
|
}
|
2009-07-03 02:44:48 +04:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
|
2011-07-27 02:44:36 +04:00
|
|
|
nomem();
|
|
|
|
if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
|
|
|
|
nomem();
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* Always add default sorting columns */
|
|
|
|
(void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
|
|
|
|
(void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
|
|
|
|
|
|
|
|
cb.cb_sortcol = sortcol;
|
2011-07-27 02:44:36 +04:00
|
|
|
cb.cb_numname = prtnum;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
cb.cb_nicenum = !parsable;
|
2011-07-27 02:44:36 +04:00
|
|
|
cb.cb_avl_pool = avl_pool;
|
|
|
|
cb.cb_avl = avl_tree;
|
|
|
|
cb.cb_sid2posix = sid2posix;
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
|
|
|
|
for (i = 0; i < USFIELD_LAST; i++)
|
|
|
|
cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
|
2009-07-03 02:44:48 +04:00
|
|
|
|
|
|
|
for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
|
2016-10-04 21:46:10 +03:00
|
|
|
if ((zfs_prop_is_user(p) &&
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
!(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
|
2016-10-04 21:46:10 +03:00
|
|
|
(zfs_prop_is_group(p) &&
|
2018-02-14 01:54:54 +03:00
|
|
|
!(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) ||
|
|
|
|
(zfs_prop_is_project(p) && types != USTYPE_PROJ))
|
2011-07-27 02:44:36 +04:00
|
|
|
continue;
|
2016-10-04 21:46:10 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
cb.cb_prop = p;
|
2019-01-25 20:47:52 +03:00
|
|
|
if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) {
|
|
|
|
zfs_close(zhp);
|
2013-07-17 00:54:29 +04:00
|
|
|
return (ret);
|
2019-01-25 20:47:52 +03:00
|
|
|
}
|
2009-07-03 02:44:48 +04:00
|
|
|
}
|
2019-01-25 20:47:52 +03:00
|
|
|
zfs_close(zhp);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* Sort the list */
|
2013-07-17 00:54:29 +04:00
|
|
|
if ((node = uu_avl_first(avl_tree)) == NULL)
|
|
|
|
return (0);
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
us_populated = B_TRUE;
|
2013-07-17 00:54:29 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
|
|
|
|
offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
|
|
|
|
list = uu_list_create(listpool, NULL, UU_DEFAULT);
|
|
|
|
uu_list_node_init(node, &node->usn_listnode, listpool);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
while (node != NULL) {
|
|
|
|
rmnode = node;
|
|
|
|
node = uu_avl_next(avl_tree, node);
|
|
|
|
uu_avl_remove(avl_tree, rmnode);
|
|
|
|
if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
|
|
|
|
uu_list_insert(list, rmnode, idx2);
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
for (node = uu_list_first(list); node != NULL;
|
|
|
|
node = uu_list_next(list, node)) {
|
|
|
|
us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
|
|
|
|
|
|
|
|
if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
|
|
|
|
uu_avl_insert(avl_tree, node, idx);
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
uu_list_destroy(list);
|
|
|
|
uu_list_pool_destroy(listpool);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
/* Print and free node nvlist memory */
|
|
|
|
print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
|
|
|
|
cb.cb_avl);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Illumos #1884, #3028, #3048, #3049, #3060, #3061, #3093
1884 Empty "used" field for zfs *space commands
3028 zfs {group,user}space -n prints (null) instead of numeric GID/UID
3048 zfs {user,group}space [-s|-S] is broken
3049 zfs {user,group}space -t doesn't really filter the results
3060 zfs {user,group}space -H output isn't tab-delimited
3061 zfs {user,group}space -o doesn't use specified fields order
3093 zfs {user,group}space's -i is noop
Reviewed by: Garry Mills <gary_mills@fastmail.fm>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
References:
illumos/illumos-gate@89f5d17b06fc4132c983112b24836a779a0ed736
illumos changeset: 13803:b5e49d71ff0e
https://www.illumos.org/issues/1884
https://www.illumos.org/issues/3028
https://www.illumos.org/issues/3048
https://www.illumos.org/issues/3049
https://www.illumos.org/issues/3060
https://www.illumos.org/issues/3061
https://www.illumos.org/issues/3093
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1194
2013-01-11 02:25:47 +04:00
|
|
|
zfs_free_sort_columns(sortcol);
|
|
|
|
|
|
|
|
/* Clean up the AVL tree */
|
2011-07-27 02:44:36 +04:00
|
|
|
if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
while ((node = uu_avl_walk_next(walk)) != NULL) {
|
|
|
|
uu_avl_remove(cb.cb_avl, node);
|
|
|
|
free(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
uu_avl_walk_end(walk);
|
|
|
|
uu_avl_destroy(avl_tree);
|
|
|
|
uu_avl_pool_destroy(avl_pool);
|
|
|
|
|
2013-07-17 00:54:29 +04:00
|
|
|
return (ret);
|
2009-07-03 02:44:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-11-19 19:41:37 +04:00
|
|
|
* list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property]
|
|
|
|
* [-t type[,...]] [filesystem|volume|snapshot] ...
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
2013-11-19 19:41:37 +04:00
|
|
|
* -H Scripted mode; elide headers and separate columns by tabs
|
|
|
|
* -p Display values in parsable (literal) format.
|
2010-05-29 00:45:14 +04:00
|
|
|
* -r Recurse over all children
|
|
|
|
* -d Limit recursion by depth.
|
|
|
|
* -o Control which fields to display.
|
2008-11-20 23:01:55 +03:00
|
|
|
* -s Specify sort columns, descending order.
|
|
|
|
* -S Specify sort columns, ascending order.
|
2013-11-19 19:41:37 +04:00
|
|
|
* -t Control which object types to display.
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
2013-11-19 19:41:37 +04:00
|
|
|
* When given no arguments, list all filesystems in the system.
|
2008-11-20 23:01:55 +03:00
|
|
|
* Otherwise, list the specified datasets, optionally recursing down them if
|
|
|
|
* '-r' is specified.
|
|
|
|
*/
|
|
|
|
typedef struct list_cbdata {
|
|
|
|
boolean_t cb_first;
|
2013-11-19 19:41:37 +04:00
|
|
|
boolean_t cb_literal;
|
2008-11-20 23:01:55 +03:00
|
|
|
boolean_t cb_scripted;
|
|
|
|
zprop_list_t *cb_proplist;
|
|
|
|
} list_cbdata_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a list of columns to display, output appropriate headers for each one.
|
|
|
|
*/
|
|
|
|
static void
|
2013-11-19 19:41:37 +04:00
|
|
|
print_header(list_cbdata_t *cb)
|
2008-11-20 23:01:55 +03:00
|
|
|
{
|
2013-11-19 19:41:37 +04:00
|
|
|
zprop_list_t *pl = cb->cb_proplist;
|
2008-11-20 23:01:55 +03:00
|
|
|
char headerbuf[ZFS_MAXPROPLEN];
|
|
|
|
const char *header;
|
|
|
|
int i;
|
|
|
|
boolean_t first = B_TRUE;
|
|
|
|
boolean_t right_justify;
|
|
|
|
|
|
|
|
for (; pl != NULL; pl = pl->pl_next) {
|
|
|
|
if (!first) {
|
|
|
|
(void) printf(" ");
|
|
|
|
} else {
|
|
|
|
first = B_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
right_justify = B_FALSE;
|
|
|
|
if (pl->pl_prop != ZPROP_INVAL) {
|
|
|
|
header = zfs_prop_column_name(pl->pl_prop);
|
|
|
|
right_justify = zfs_prop_align_right(pl->pl_prop);
|
|
|
|
} else {
|
|
|
|
for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
|
|
|
|
headerbuf[i] = toupper(pl->pl_user_prop[i]);
|
|
|
|
headerbuf[i] = '\0';
|
|
|
|
header = headerbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl->pl_next == NULL && !right_justify)
|
|
|
|
(void) printf("%s", header);
|
|
|
|
else if (right_justify)
|
2010-08-26 20:52:39 +04:00
|
|
|
(void) printf("%*s", (int)pl->pl_width, header);
|
2008-11-20 23:01:55 +03:00
|
|
|
else
|
2010-08-26 20:52:39 +04:00
|
|
|
(void) printf("%-*s", (int)pl->pl_width, header);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a dataset and a list of fields, print out all the properties according
|
|
|
|
* to the described layout.
|
|
|
|
*/
|
|
|
|
static void
|
2013-11-19 19:41:37 +04:00
|
|
|
print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
|
2008-11-20 23:01:55 +03:00
|
|
|
{
|
2013-11-19 19:41:37 +04:00
|
|
|
zprop_list_t *pl = cb->cb_proplist;
|
2008-11-20 23:01:55 +03:00
|
|
|
boolean_t first = B_TRUE;
|
|
|
|
char property[ZFS_MAXPROPLEN];
|
|
|
|
nvlist_t *userprops = zfs_get_user_props(zhp);
|
|
|
|
nvlist_t *propval;
|
|
|
|
char *propstr;
|
|
|
|
boolean_t right_justify;
|
|
|
|
|
|
|
|
for (; pl != NULL; pl = pl->pl_next) {
|
|
|
|
if (!first) {
|
2013-11-19 19:41:37 +04:00
|
|
|
if (cb->cb_scripted)
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) printf("\t");
|
|
|
|
else
|
|
|
|
(void) printf(" ");
|
|
|
|
} else {
|
|
|
|
first = B_FALSE;
|
|
|
|
}
|
|
|
|
|
2012-05-29 21:50:50 +04:00
|
|
|
if (pl->pl_prop == ZFS_PROP_NAME) {
|
|
|
|
(void) strlcpy(property, zfs_get_name(zhp),
|
2013-11-01 23:26:11 +04:00
|
|
|
sizeof (property));
|
2012-05-29 21:50:50 +04:00
|
|
|
propstr = property;
|
|
|
|
right_justify = zfs_prop_align_right(pl->pl_prop);
|
|
|
|
} else if (pl->pl_prop != ZPROP_INVAL) {
|
2008-11-20 23:01:55 +03:00
|
|
|
if (zfs_prop_get(zhp, pl->pl_prop, property,
|
2013-11-19 19:41:37 +04:00
|
|
|
sizeof (property), NULL, NULL, 0,
|
2013-11-01 23:26:11 +04:00
|
|
|
cb->cb_literal) != 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
propstr = "-";
|
|
|
|
else
|
|
|
|
propstr = property;
|
|
|
|
right_justify = zfs_prop_align_right(pl->pl_prop);
|
2009-07-03 02:44:48 +04:00
|
|
|
} else if (zfs_prop_userquota(pl->pl_user_prop)) {
|
|
|
|
if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
|
2013-11-19 19:41:37 +04:00
|
|
|
property, sizeof (property), cb->cb_literal) != 0)
|
2009-07-03 02:44:48 +04:00
|
|
|
propstr = "-";
|
|
|
|
else
|
|
|
|
propstr = property;
|
|
|
|
right_justify = B_TRUE;
|
2011-11-17 22:14:36 +04:00
|
|
|
} else if (zfs_prop_written(pl->pl_user_prop)) {
|
|
|
|
if (zfs_prop_get_written(zhp, pl->pl_user_prop,
|
2013-11-19 19:41:37 +04:00
|
|
|
property, sizeof (property), cb->cb_literal) != 0)
|
2011-11-17 22:14:36 +04:00
|
|
|
propstr = "-";
|
|
|
|
else
|
|
|
|
propstr = property;
|
|
|
|
right_justify = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
} else {
|
|
|
|
if (nvlist_lookup_nvlist(userprops,
|
|
|
|
pl->pl_user_prop, &propval) != 0)
|
|
|
|
propstr = "-";
|
|
|
|
else
|
|
|
|
verify(nvlist_lookup_string(propval,
|
|
|
|
ZPROP_VALUE, &propstr) == 0);
|
2009-07-03 02:44:48 +04:00
|
|
|
right_justify = B_FALSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is being called in scripted mode, or if this is the
|
|
|
|
* last column and it is left-justified, don't include a width
|
|
|
|
* format specifier.
|
|
|
|
*/
|
2013-11-19 19:41:37 +04:00
|
|
|
if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) printf("%s", propstr);
|
|
|
|
else if (right_justify)
|
2013-11-19 19:41:37 +04:00
|
|
|
(void) printf("%*s", (int)pl->pl_width, propstr);
|
2008-11-20 23:01:55 +03:00
|
|
|
else
|
2013-11-19 19:41:37 +04:00
|
|
|
(void) printf("%-*s", (int)pl->pl_width, propstr);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic callback function to list a dataset or snapshot.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
list_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
list_cbdata_t *cbp = data;
|
|
|
|
|
|
|
|
if (cbp->cb_first) {
|
|
|
|
if (!cbp->cb_scripted)
|
2013-11-19 19:41:37 +04:00
|
|
|
print_header(cbp);
|
2008-11-20 23:01:55 +03:00
|
|
|
cbp->cb_first = B_FALSE;
|
|
|
|
}
|
|
|
|
|
2013-11-19 19:41:37 +04:00
|
|
|
print_dataset(zhp, cbp);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_list(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
static char default_fields[] =
|
|
|
|
"name,used,available,referenced,mountpoint";
|
2009-02-18 23:51:31 +03:00
|
|
|
int types = ZFS_TYPE_DATASET;
|
2008-12-03 23:09:06 +03:00
|
|
|
boolean_t types_specified = B_FALSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
char *fields = NULL;
|
|
|
|
list_cbdata_t cb = { 0 };
|
|
|
|
char *value;
|
2009-07-03 02:44:48 +04:00
|
|
|
int limit = 0;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_sort_column_t *sortcol = NULL;
|
2008-12-03 23:09:06 +03:00
|
|
|
int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
2013-11-19 19:41:37 +04:00
|
|
|
while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
|
|
|
case 'o':
|
|
|
|
fields = optarg;
|
|
|
|
break;
|
2013-11-19 19:41:37 +04:00
|
|
|
case 'p':
|
|
|
|
cb.cb_literal = B_TRUE;
|
|
|
|
flags |= ZFS_ITER_LITERAL_PROPS;
|
|
|
|
break;
|
2009-07-03 02:44:48 +04:00
|
|
|
case 'd':
|
|
|
|
limit = parse_depth(optarg, &flags);
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'r':
|
2008-12-03 23:09:06 +03:00
|
|
|
flags |= ZFS_ITER_RECURSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 'H':
|
2013-11-19 19:41:37 +04:00
|
|
|
cb.cb_scripted = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if (zfs_add_sort_column(&sortcol, optarg,
|
|
|
|
B_FALSE) != 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid property '%s'\n"), optarg);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
if (zfs_add_sort_column(&sortcol, optarg,
|
|
|
|
B_TRUE) != 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid property '%s'\n"), optarg);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
types = 0;
|
2008-12-03 23:09:06 +03:00
|
|
|
types_specified = B_TRUE;
|
|
|
|
flags &= ~ZFS_ITER_PROP_LISTSNAPS;
|
2008-11-20 23:01:55 +03:00
|
|
|
while (*optarg != '\0') {
|
2008-12-03 23:09:06 +03:00
|
|
|
static char *type_subopts[] = { "filesystem",
|
2013-12-12 02:33:41 +04:00
|
|
|
"volume", "snapshot", "snap", "bookmark",
|
|
|
|
"all", NULL };
|
2008-12-03 23:09:06 +03:00
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (getsubopt(&optarg, type_subopts,
|
|
|
|
&value)) {
|
|
|
|
case 0:
|
|
|
|
types |= ZFS_TYPE_FILESYSTEM;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
types |= ZFS_TYPE_VOLUME;
|
|
|
|
break;
|
|
|
|
case 2:
|
2012-04-09 10:05:54 +04:00
|
|
|
case 3:
|
2008-11-20 23:01:55 +03:00
|
|
|
types |= ZFS_TYPE_SNAPSHOT;
|
|
|
|
break;
|
2012-04-09 10:05:54 +04:00
|
|
|
case 4:
|
2013-12-12 02:33:41 +04:00
|
|
|
types |= ZFS_TYPE_BOOKMARK;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
types = ZFS_TYPE_DATASET |
|
|
|
|
ZFS_TYPE_BOOKMARK;
|
2008-12-03 23:09:06 +03:00
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
default:
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid type '%s'\n"),
|
|
|
|
value);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing argument for "
|
|
|
|
"'%c' option\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (fields == NULL)
|
2008-12-03 23:09:06 +03:00
|
|
|
fields = default_fields;
|
|
|
|
|
2012-05-29 21:50:50 +04:00
|
|
|
/*
|
|
|
|
* If we are only going to list snapshot names and sort by name,
|
|
|
|
* then we can use faster version.
|
|
|
|
*/
|
|
|
|
if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol))
|
|
|
|
flags |= ZFS_ITER_SIMPLE;
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
/*
|
|
|
|
* If "-o space" and no types were specified, don't display snapshots.
|
|
|
|
*/
|
|
|
|
if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
|
|
|
|
types &= ~ZFS_TYPE_SNAPSHOT;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2019-04-01 21:58:59 +03:00
|
|
|
/*
|
2019-11-19 03:44:28 +03:00
|
|
|
* Handle users who want to list all snapshots or bookmarks
|
|
|
|
* of the current dataset (ex. 'zfs list -t snapshot <dataset>').
|
2019-04-01 21:58:59 +03:00
|
|
|
*/
|
2019-11-19 03:44:28 +03:00
|
|
|
if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&
|
|
|
|
argc > 0 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {
|
2019-04-01 21:58:59 +03:00
|
|
|
flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);
|
|
|
|
limit = 1;
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
|
|
|
* If the user specifies '-o all', the zprop_get_list() doesn't
|
|
|
|
* normally include the name of the dataset. For 'zfs list', we always
|
|
|
|
* want this property to be first.
|
|
|
|
*/
|
|
|
|
if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
|
|
|
|
!= 0)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
|
|
|
cb.cb_first = B_TRUE;
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
|
2009-07-03 02:44:48 +04:00
|
|
|
limit, list_callback, &cb);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
zprop_free_list(cb.cb_proplist);
|
|
|
|
zfs_free_sort_columns(sortcol);
|
|
|
|
|
|
|
|
if (ret == 0 && cb.cb_first && !cb.cb_scripted)
|
2012-03-14 17:34:00 +04:00
|
|
|
(void) fprintf(stderr, gettext("no datasets available\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-04-27 22:14:46 +04:00
|
|
|
* zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
|
|
|
|
* zfs rename [-f] -p <fs | vol> <fs | vol>
|
2008-11-20 23:01:55 +03:00
|
|
|
* zfs rename -r <snap> <snap>
|
|
|
|
*
|
|
|
|
* Renames the given dataset to another of the same type.
|
|
|
|
*
|
|
|
|
* The '-p' flag creates all the non-existing ancestors of the target first.
|
|
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
zfs_do_rename(int argc, char **argv)
|
|
|
|
{
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
int c;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
boolean_t recurse = B_FALSE;
|
|
|
|
boolean_t parents = B_FALSE;
|
2012-04-27 22:14:46 +04:00
|
|
|
boolean_t force_unmount = B_FALSE;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
2012-04-27 22:14:46 +04:00
|
|
|
while ((c = getopt(argc, argv, "prf")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
|
|
|
case 'p':
|
|
|
|
parents = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
recurse = B_TRUE;
|
|
|
|
break;
|
2012-04-27 22:14:46 +04:00
|
|
|
case 'f':
|
|
|
|
force_unmount = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing source dataset "
|
|
|
|
"argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc < 2) {
|
|
|
|
(void) fprintf(stderr, gettext("missing target dataset "
|
|
|
|
"argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 2) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recurse && parents) {
|
|
|
|
(void) fprintf(stderr, gettext("-p and -r options are mutually "
|
|
|
|
"exclusive\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recurse && strchr(argv[0], '@') == 0) {
|
|
|
|
(void) fprintf(stderr, gettext("source dataset for recursive "
|
|
|
|
"rename must be a snapshot\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM |
|
|
|
|
ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
/* If we were asked and the name looks good, try to create ancestors. */
|
|
|
|
if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
|
|
|
|
zfs_create_ancestors(g_zfs, argv[1]) != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2012-04-27 22:14:46 +04:00
|
|
|
ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* zfs promote <fs>
|
|
|
|
*
|
|
|
|
* Promotes the given clone fs to be the parent
|
|
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
zfs_do_promote(int argc, char **argv)
|
|
|
|
{
|
|
|
|
zfs_handle_t *zhp;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
|
|
|
if (argc > 1 && argv[1][0] == '-') {
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
argv[1][1]);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 2) {
|
|
|
|
(void) fprintf(stderr, gettext("missing clone filesystem"
|
|
|
|
" argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 2) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
ret = (zfs_promote(zhp) != 0);
|
|
|
|
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
static int
|
|
|
|
zfs_do_redact(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *snap = NULL;
|
|
|
|
char *bookname = NULL;
|
|
|
|
char **rsnaps = NULL;
|
|
|
|
int numrsnaps = 0;
|
|
|
|
argv++;
|
|
|
|
argc--;
|
|
|
|
if (argc < 3) {
|
2019-07-06 02:38:17 +03:00
|
|
|
(void) fprintf(stderr, gettext("too few arguments\n"));
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
snap = argv[0];
|
|
|
|
bookname = argv[1];
|
|
|
|
rsnaps = argv + 2;
|
|
|
|
numrsnaps = argc - 2;
|
|
|
|
|
|
|
|
nvlist_t *rsnapnv = fnvlist_alloc();
|
|
|
|
|
|
|
|
for (int i = 0; i < numrsnaps; i++) {
|
|
|
|
fnvlist_add_boolean(rsnapnv, rsnaps[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int err = lzc_redact(snap, bookname, rsnapnv);
|
|
|
|
fnvlist_free(rsnapnv);
|
|
|
|
|
|
|
|
switch (err) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case ENOENT:
|
|
|
|
(void) fprintf(stderr,
|
2019-07-06 02:38:17 +03:00
|
|
|
gettext("provided snapshot %s does not exist\n"), snap);
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
break;
|
|
|
|
case EEXIST:
|
|
|
|
(void) fprintf(stderr, gettext("specified redaction bookmark "
|
2019-07-06 02:38:17 +03:00
|
|
|
"(%s) provided already exists\n"), bookname);
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
break;
|
|
|
|
case ENAMETOOLONG:
|
|
|
|
(void) fprintf(stderr, gettext("provided bookmark name cannot "
|
2019-07-06 02:38:17 +03:00
|
|
|
"be used, final name would be too long\n"));
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
break;
|
|
|
|
case E2BIG:
|
|
|
|
(void) fprintf(stderr, gettext("too many redaction snapshots "
|
2019-07-06 02:38:17 +03:00
|
|
|
"specified\n"));
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
break;
|
|
|
|
case EINVAL:
|
2019-11-25 22:08:20 +03:00
|
|
|
if (strchr(bookname, '#') != NULL)
|
|
|
|
(void) fprintf(stderr, gettext(
|
|
|
|
"redaction bookmark name must not contain '#'\n"));
|
|
|
|
else
|
|
|
|
(void) fprintf(stderr, gettext(
|
|
|
|
"redaction snapshot must be descendent of "
|
|
|
|
"snapshot being redacted\n"));
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
break;
|
|
|
|
case EALREADY:
|
|
|
|
(void) fprintf(stderr, gettext("attempted to redact redacted "
|
2019-07-06 02:38:17 +03:00
|
|
|
"dataset or with respect to redacted dataset\n"));
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
break;
|
|
|
|
case ENOTSUP:
|
|
|
|
(void) fprintf(stderr, gettext("redaction bookmarks feature "
|
2019-07-06 02:38:17 +03:00
|
|
|
"not enabled\n"));
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
break;
|
2019-11-25 22:08:20 +03:00
|
|
|
case EXDEV:
|
|
|
|
(void) fprintf(stderr, gettext("potentially invalid redaction "
|
|
|
|
"snapshot; full dataset names required\n"));
|
|
|
|
break;
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
default:
|
2019-07-06 02:38:17 +03:00
|
|
|
(void) fprintf(stderr, gettext("internal error: %s\n"),
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
|
|
|
* zfs rollback [-rRf] <snapshot>
|
|
|
|
*
|
2010-05-29 00:45:14 +04:00
|
|
|
* -r Delete any intervening snapshots before doing rollback
|
|
|
|
* -R Delete any snapshots and their clones
|
2017-01-03 20:31:18 +03:00
|
|
|
* -f ignored for backwards compatibility
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* Given a filesystem, rollback to a specific snapshot, discarding any changes
|
|
|
|
* since then and making it the active dataset. If more recent snapshots exist,
|
|
|
|
* the command will complain unless the '-r' flag is given.
|
|
|
|
*/
|
|
|
|
typedef struct rollback_cbdata {
|
|
|
|
uint64_t cb_create;
|
2019-03-12 23:13:22 +03:00
|
|
|
uint8_t cb_younger_ds_printed;
|
2008-11-20 23:01:55 +03:00
|
|
|
boolean_t cb_first;
|
|
|
|
int cb_doclones;
|
|
|
|
char *cb_target;
|
|
|
|
int cb_error;
|
|
|
|
boolean_t cb_recurse;
|
|
|
|
} rollback_cbdata_t;
|
|
|
|
|
2013-12-12 02:33:41 +04:00
|
|
|
static int
|
|
|
|
rollback_check_dependent(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
rollback_cbdata_t *cbp = data;
|
|
|
|
|
|
|
|
if (cbp->cb_first && cbp->cb_recurse) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot rollback to "
|
|
|
|
"'%s': clones of previous snapshots exist\n"),
|
|
|
|
cbp->cb_target);
|
|
|
|
(void) fprintf(stderr, gettext("use '-R' to "
|
|
|
|
"force deletion of the following clones and "
|
|
|
|
"dependents:\n"));
|
|
|
|
cbp->cb_first = 0;
|
|
|
|
cbp->cb_error = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
2014-06-06 01:19:08 +04:00
|
|
|
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
2019-03-12 23:13:22 +03:00
|
|
|
* Report some snapshots/bookmarks more recent than the one specified.
|
|
|
|
* Used when '-r' is not specified. We reuse this same callback for the
|
|
|
|
* snapshot dependents - if 'cb_dependent' is set, then this is a
|
|
|
|
* dependent and we should report it without checking the transaction group.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
rollback_check(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
rollback_cbdata_t *cbp = data;
|
2019-03-12 23:13:22 +03:00
|
|
|
/*
|
|
|
|
* Max number of younger snapshots and/or bookmarks to display before
|
|
|
|
* we stop the iteration.
|
|
|
|
*/
|
|
|
|
const uint8_t max_younger = 32;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (cbp->cb_doclones) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2013-12-12 02:33:41 +04:00
|
|
|
if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
|
|
|
|
if (cbp->cb_first && !cbp->cb_recurse) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"rollback to '%s': more recent snapshots "
|
|
|
|
"or bookmarks exist\n"),
|
2008-11-20 23:01:55 +03:00
|
|
|
cbp->cb_target);
|
2013-12-12 02:33:41 +04:00
|
|
|
(void) fprintf(stderr, gettext("use '-r' to "
|
|
|
|
"force deletion of the following "
|
|
|
|
"snapshots and bookmarks:\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
cbp->cb_first = 0;
|
|
|
|
cbp->cb_error = 1;
|
|
|
|
}
|
|
|
|
|
2013-12-12 02:33:41 +04:00
|
|
|
if (cbp->cb_recurse) {
|
|
|
|
if (zfs_iter_dependents(zhp, B_TRUE,
|
|
|
|
rollback_check_dependent, cbp) != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr, "%s\n",
|
|
|
|
zfs_get_name(zhp));
|
2019-03-12 23:13:22 +03:00
|
|
|
cbp->cb_younger_ds_printed++;
|
2013-12-12 02:33:41 +04:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
zfs_close(zhp);
|
2019-03-12 23:13:22 +03:00
|
|
|
|
|
|
|
if (cbp->cb_younger_ds_printed == max_younger) {
|
|
|
|
/*
|
|
|
|
* This non-recursive rollback is going to fail due to the
|
|
|
|
* presence of snapshots and/or bookmarks that are younger than
|
|
|
|
* the rollback target.
|
|
|
|
* We printed some of the offending objects, now we stop
|
|
|
|
* zfs_iter_snapshot/bookmark iteration so we can fail fast and
|
|
|
|
* avoid iterating over the rest of the younger objects
|
|
|
|
*/
|
|
|
|
(void) fprintf(stderr, gettext("Output limited to %d "
|
|
|
|
"snapshots/bookmarks\n"), max_younger);
|
|
|
|
return (-1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_rollback(int argc, char **argv)
|
|
|
|
{
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
int c;
|
|
|
|
boolean_t force = B_FALSE;
|
|
|
|
rollback_cbdata_t cb = { 0 };
|
|
|
|
zfs_handle_t *zhp, *snap;
|
2016-06-16 00:28:36 +03:00
|
|
|
char parentname[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 23:01:55 +03:00
|
|
|
char *delim;
|
2019-03-12 23:13:22 +03:00
|
|
|
uint64_t min_txg = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
|
|
|
while ((c = getopt(argc, argv, "rRf")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
|
|
|
cb.cb_recurse = 1;
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
cb.cb_recurse = 1;
|
|
|
|
cb.cb_doclones = 1;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
force = B_TRUE;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing dataset argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open the snapshot */
|
|
|
|
if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
/* open the parent dataset */
|
|
|
|
(void) strlcpy(parentname, argv[0], sizeof (parentname));
|
|
|
|
verify((delim = strrchr(parentname, '@')) != NULL);
|
|
|
|
*delim = '\0';
|
|
|
|
if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
|
|
|
|
zfs_close(snap);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for more recent snapshots and/or clones based on the presence
|
|
|
|
* of '-r' and '-R'.
|
|
|
|
*/
|
|
|
|
cb.cb_target = argv[0];
|
|
|
|
cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
|
|
|
|
cb.cb_first = B_TRUE;
|
|
|
|
cb.cb_error = 0;
|
2019-03-12 23:13:22 +03:00
|
|
|
|
|
|
|
if (cb.cb_create > 0)
|
|
|
|
min_txg = cb.cb_create;
|
|
|
|
|
|
|
|
if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb,
|
|
|
|
min_txg, 0)) != 0)
|
2013-12-12 02:33:41 +04:00
|
|
|
goto out;
|
|
|
|
if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if ((ret = cb.cb_error) != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rollback parent to the given snapshot.
|
|
|
|
*/
|
|
|
|
ret = zfs_rollback(zhp, snap, force);
|
|
|
|
|
|
|
|
out:
|
|
|
|
zfs_close(snap);
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
return (0);
|
|
|
|
else
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-07-06 02:11:09 +03:00
|
|
|
* zfs set property=value ... { fs | snap | vol } ...
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
2015-07-06 02:11:09 +03:00
|
|
|
* Sets the given properties for all datasets specified on the command line.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
2015-07-06 02:11:09 +03:00
|
|
|
nvlist_t *props = data;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2015-07-06 02:11:09 +03:00
|
|
|
if (zfs_prop_set_list(zhp, props) != 0) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (libzfs_errno(g_zfs)) {
|
|
|
|
case EZFS_MOUNTFAILED:
|
|
|
|
(void) fprintf(stderr, gettext("property may be set "
|
|
|
|
"but unable to remount filesystem\n"));
|
|
|
|
break;
|
|
|
|
case EZFS_SHARENFSFAILED:
|
|
|
|
(void) fprintf(stderr, gettext("property may be set "
|
|
|
|
"but unable to reshare filesystem\n"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_set(int argc, char **argv)
|
|
|
|
{
|
2015-07-06 02:11:09 +03:00
|
|
|
nvlist_t *props = NULL;
|
|
|
|
int ds_start = -1; /* argv idx of first dataset arg */
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2015-07-06 02:11:09 +03:00
|
|
|
int i;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check for options */
|
|
|
|
if (argc > 1 && argv[1][0] == '-') {
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
argv[1][1]);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 2) {
|
2015-07-06 02:11:09 +03:00
|
|
|
(void) fprintf(stderr, gettext("missing arguments\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc < 3) {
|
2015-07-06 02:11:09 +03:00
|
|
|
if (strchr(argv[1], '=') == NULL) {
|
|
|
|
(void) fprintf(stderr, gettext("missing property=value "
|
|
|
|
"argument(s)\n"));
|
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr, gettext("missing dataset "
|
|
|
|
"name(s)\n"));
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2015-07-06 02:11:09 +03:00
|
|
|
/* validate argument order: prop=val args followed by dataset args */
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
if (strchr(argv[i], '=') != NULL) {
|
|
|
|
if (ds_start > 0) {
|
|
|
|
/* out-of-order prop=val argument */
|
|
|
|
(void) fprintf(stderr, gettext("invalid "
|
|
|
|
"argument order\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
} else if (ds_start < 0) {
|
|
|
|
ds_start = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ds_start < 0) {
|
|
|
|
(void) fprintf(stderr, gettext("missing dataset name(s)\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2015-07-06 02:11:09 +03:00
|
|
|
/* Populate a list of property settings */
|
|
|
|
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
|
|
|
for (i = 1; i < ds_start; i++) {
|
2017-05-10 02:21:09 +03:00
|
|
|
if (!parseprop(props, argv[i])) {
|
|
|
|
ret = -1;
|
2015-07-06 02:11:09 +03:00
|
|
|
goto error;
|
2017-05-10 02:21:09 +03:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2015-07-06 02:11:09 +03:00
|
|
|
ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,
|
|
|
|
ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2015-07-06 02:11:09 +03:00
|
|
|
error:
|
|
|
|
nvlist_free(props);
|
2008-11-20 23:01:55 +03:00
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2013-08-28 15:45:09 +04:00
|
|
|
typedef struct snap_cbdata {
|
|
|
|
nvlist_t *sd_nvl;
|
|
|
|
boolean_t sd_recursive;
|
|
|
|
const char *sd_snapname;
|
|
|
|
} snap_cbdata_t;
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
snap_cbdata_t *sd = arg;
|
|
|
|
char *name;
|
|
|
|
int rv = 0;
|
|
|
|
int error;
|
|
|
|
|
2013-07-27 21:51:50 +04:00
|
|
|
if (sd->sd_recursive &&
|
|
|
|
zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2013-08-28 15:45:09 +04:00
|
|
|
error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
|
|
|
|
if (error == -1)
|
|
|
|
nomem();
|
|
|
|
fnvlist_add_boolean(sd->sd_nvl, name);
|
|
|
|
free(name);
|
|
|
|
|
|
|
|
if (sd->sd_recursive)
|
|
|
|
rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
2008-12-03 23:09:06 +03:00
|
|
|
* zfs snapshot [-r] [-o prop=value] ... <fs@snap>
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* Creates a snapshot with the given name. While functionally equivalent to
|
|
|
|
* 'zfs create', it is a separate command to differentiate intent.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_snapshot(int argc, char **argv)
|
|
|
|
{
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2019-05-28 21:14:58 +03:00
|
|
|
int c;
|
2008-12-03 23:09:06 +03:00
|
|
|
nvlist_t *props;
|
2013-08-28 15:45:09 +04:00
|
|
|
snap_cbdata_t sd = { 0 };
|
|
|
|
boolean_t multiple_snaps = B_FALSE;
|
2008-12-03 23:09:06 +03:00
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
2013-08-28 15:45:09 +04:00
|
|
|
if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
2008-12-03 23:09:06 +03:00
|
|
|
while ((c = getopt(argc, argv, "ro:")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
2008-12-03 23:09:06 +03:00
|
|
|
case 'o':
|
2017-05-10 02:21:09 +03:00
|
|
|
if (!parseprop(props, optarg)) {
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(sd.sd_nvl);
|
|
|
|
nvlist_free(props);
|
2008-12-03 23:09:06 +03:00
|
|
|
return (1);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
2008-12-03 23:09:06 +03:00
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'r':
|
2013-08-28 15:45:09 +04:00
|
|
|
sd.sd_recursive = B_TRUE;
|
|
|
|
multiple_snaps = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
2008-12-03 23:09:06 +03:00
|
|
|
goto usage;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
|
2008-12-03 23:09:06 +03:00
|
|
|
goto usage;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2013-08-28 15:45:09 +04:00
|
|
|
|
|
|
|
if (argc > 1)
|
|
|
|
multiple_snaps = B_TRUE;
|
|
|
|
for (; argc > 0; argc--, argv++) {
|
|
|
|
char *atp;
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
|
|
|
|
atp = strchr(argv[0], '@');
|
|
|
|
if (atp == NULL)
|
|
|
|
goto usage;
|
|
|
|
*atp = '\0';
|
|
|
|
sd.sd_snapname = atp + 1;
|
|
|
|
zhp = zfs_open(g_zfs, argv[0],
|
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL)
|
|
|
|
goto usage;
|
|
|
|
if (zfs_snapshot_cb(zhp, &sd) != 0)
|
|
|
|
goto usage;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2013-08-28 15:45:09 +04:00
|
|
|
ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
|
|
|
|
nvlist_free(sd.sd_nvl);
|
2008-12-03 23:09:06 +03:00
|
|
|
nvlist_free(props);
|
2013-08-28 15:45:09 +04:00
|
|
|
if (ret != 0 && multiple_snaps)
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(stderr, gettext("no snapshots were created\n"));
|
|
|
|
return (ret != 0);
|
2008-12-03 23:09:06 +03:00
|
|
|
|
|
|
|
usage:
|
2013-08-28 15:45:09 +04:00
|
|
|
nvlist_free(sd.sd_nvl);
|
2008-12-03 23:09:06 +03:00
|
|
|
nvlist_free(props);
|
|
|
|
usage(B_FALSE);
|
|
|
|
return (-1);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
|
|
|
* Send a backup stream to stdout.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_send(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *fromname = NULL;
|
|
|
|
char *toname = NULL;
|
2016-01-07 00:22:48 +03:00
|
|
|
char *resume_token = NULL;
|
2008-11-20 23:01:55 +03:00
|
|
|
char *cp;
|
|
|
|
zfs_handle_t *zhp;
|
2010-05-29 00:45:14 +04:00
|
|
|
sendflags_t flags = { 0 };
|
2008-11-20 23:01:55 +03:00
|
|
|
int c, err;
|
2011-11-17 22:14:36 +04:00
|
|
|
nvlist_t *dbgnv = NULL;
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
char *redactbook = NULL;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2017-04-12 00:56:54 +03:00
|
|
|
struct option long_options[] = {
|
|
|
|
{"replicate", no_argument, NULL, 'R'},
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
{"redact", required_argument, NULL, 'd'},
|
2017-04-12 00:56:54 +03:00
|
|
|
{"props", no_argument, NULL, 'p'},
|
|
|
|
{"parsable", no_argument, NULL, 'P'},
|
|
|
|
{"dedup", no_argument, NULL, 'D'},
|
|
|
|
{"verbose", no_argument, NULL, 'v'},
|
|
|
|
{"dryrun", no_argument, NULL, 'n'},
|
|
|
|
{"large-block", no_argument, NULL, 'L'},
|
|
|
|
{"embed", no_argument, NULL, 'e'},
|
|
|
|
{"resume", required_argument, NULL, 't'},
|
|
|
|
{"compressed", no_argument, NULL, 'c'},
|
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 20:36:48 +03:00
|
|
|
{"raw", no_argument, NULL, 'w'},
|
2018-02-21 23:32:06 +03:00
|
|
|
{"backup", no_argument, NULL, 'b'},
|
2019-02-15 23:41:38 +03:00
|
|
|
{"holds", no_argument, NULL, 'h'},
|
2020-01-10 21:16:58 +03:00
|
|
|
{"saved", no_argument, NULL, 'S'},
|
2017-04-12 00:56:54 +03:00
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/* check options */
|
2020-01-10 21:16:58 +03:00
|
|
|
while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLeht:cwbd:S",
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
long_options, NULL)) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
|
|
|
case 'i':
|
|
|
|
if (fromname)
|
|
|
|
usage(B_FALSE);
|
|
|
|
fromname = optarg;
|
|
|
|
break;
|
|
|
|
case 'I':
|
|
|
|
if (fromname)
|
|
|
|
usage(B_FALSE);
|
|
|
|
fromname = optarg;
|
2010-05-29 00:45:14 +04:00
|
|
|
flags.doall = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case 'R':
|
2010-05-29 00:45:14 +04:00
|
|
|
flags.replicate = B_TRUE;
|
|
|
|
break;
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
case 'd':
|
|
|
|
redactbook = optarg;
|
|
|
|
break;
|
2010-05-29 00:45:14 +04:00
|
|
|
case 'p':
|
|
|
|
flags.props = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2018-02-21 23:32:06 +03:00
|
|
|
case 'b':
|
|
|
|
flags.backup = B_TRUE;
|
|
|
|
break;
|
2019-02-15 23:41:38 +03:00
|
|
|
case 'h':
|
|
|
|
flags.holds = B_TRUE;
|
|
|
|
break;
|
2011-11-17 22:14:36 +04:00
|
|
|
case 'P':
|
|
|
|
flags.parsable = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'v':
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
flags.verbosity++;
|
2012-05-10 02:05:14 +04:00
|
|
|
flags.progress = B_TRUE;
|
2010-05-29 00:45:14 +04:00
|
|
|
break;
|
|
|
|
case 'D':
|
2020-04-23 20:06:57 +03:00
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("WARNING: deduplicated send is no "
|
|
|
|
"longer supported. A regular,\n"
|
|
|
|
"non-deduplicated stream will be generated.\n\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2011-11-17 22:14:36 +04:00
|
|
|
case 'n':
|
|
|
|
flags.dryrun = B_TRUE;
|
|
|
|
break;
|
2014-11-03 23:15:08 +03:00
|
|
|
case 'L':
|
|
|
|
flags.largeblock = B_TRUE;
|
|
|
|
break;
|
2014-06-06 01:19:08 +04:00
|
|
|
case 'e':
|
|
|
|
flags.embed_data = B_TRUE;
|
|
|
|
break;
|
2016-01-07 00:22:48 +03:00
|
|
|
case 't':
|
|
|
|
resume_token = optarg;
|
|
|
|
break;
|
2016-07-11 20:45:52 +03:00
|
|
|
case 'c':
|
|
|
|
flags.compress = B_TRUE;
|
|
|
|
break;
|
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 20:36:48 +03:00
|
|
|
case 'w':
|
|
|
|
flags.raw = B_TRUE;
|
|
|
|
flags.compress = B_TRUE;
|
|
|
|
flags.embed_data = B_TRUE;
|
|
|
|
flags.largeblock = B_TRUE;
|
|
|
|
break;
|
2020-01-10 21:16:58 +03:00
|
|
|
case 'S':
|
|
|
|
flags.saved = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case ':':
|
OpenZFS 7742 - zfs send wrong error message with invalid long opts
There are two cases:
1. if an invalid flag is passed, and
2. if a valid flag is not given a parameter.
In the case of (1), the flag is either short or long. For short flags,
optopt contains the character of the flag. For long, it contains zero,
and we can access the long flag using argv and optind.
In the case of (2), if the flag is short, optopt contains the character
of the flag. If the flag is long, the value in the 4th column of the
long_options table, for that flag, is returned.
We could case over all those values, or we could simply use argv and
optind again.
Note that in the case of something like `--resume`, which is also `-t`,
"t" will be returned if an argument is not provided; so the error
message will say `'t': argument not provided` or similar. This could be
fixed by making it so long and short options don't use the same
character flag, and then combining them in the switch/case statement,
but I didn't think the ugliness of the code would be worth the small
usability enhancement.
Authored by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed by: Steve Gonczi <steve.gonczi@delphix.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Ported-by: George Melikov <mail@gmelikov.ru>
OpenZFS-issue: https://www.illumos.org/issues/7742
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/6d69b40
Closes #5702
2017-02-01 01:41:23 +03:00
|
|
|
/*
|
|
|
|
* If a parameter was not passed, optopt contains the
|
|
|
|
* value that would normally lead us into the
|
|
|
|
* appropriate case statement. If it's > 256, then this
|
|
|
|
* must be a longopt and we should look at argv to get
|
|
|
|
* the string. Otherwise it's just the character, so we
|
|
|
|
* should use it directly.
|
|
|
|
*/
|
|
|
|
if (optopt <= UINT8_MAX) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("missing argument for '%c' "
|
|
|
|
"option\n"), optopt);
|
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("missing argument for '%s' "
|
|
|
|
"option\n"), argv[optind - 1]);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
|
|
|
case '?':
|
OpenZFS 7742 - zfs send wrong error message with invalid long opts
There are two cases:
1. if an invalid flag is passed, and
2. if a valid flag is not given a parameter.
In the case of (1), the flag is either short or long. For short flags,
optopt contains the character of the flag. For long, it contains zero,
and we can access the long flag using argv and optind.
In the case of (2), if the flag is short, optopt contains the character
of the flag. If the flag is long, the value in the 4th column of the
long_options table, for that flag, is returned.
We could case over all those values, or we could simply use argv and
optind again.
Note that in the case of something like `--resume`, which is also `-t`,
"t" will be returned if an argument is not provided; so the error
message will say `'t': argument not provided` or similar. This could be
fixed by making it so long and short options don't use the same
character flag, and then combining them in the switch/case statement,
but I didn't think the ugliness of the code would be worth the small
usability enhancement.
Authored by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed by: Steve Gonczi <steve.gonczi@delphix.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Ported-by: George Melikov <mail@gmelikov.ru>
OpenZFS-issue: https://www.illumos.org/issues/7742
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/6d69b40
Closes #5702
2017-02-01 01:41:23 +03:00
|
|
|
/*FALLTHROUGH*/
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* If an invalid flag was passed, optopt contains the
|
|
|
|
* character if it was a short flag, or 0 if it was a
|
|
|
|
* longopt.
|
|
|
|
*/
|
|
|
|
if (optopt != 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option '%c'\n"), optopt);
|
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option '%s'\n"),
|
|
|
|
argv[optind - 1]);
|
|
|
|
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (flags.parsable && flags.verbosity == 0)
|
|
|
|
flags.verbosity = 1;
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
2016-01-07 00:22:48 +03:00
|
|
|
if (resume_token != NULL) {
|
|
|
|
if (fromname != NULL || flags.replicate || flags.props ||
|
2020-04-23 20:06:57 +03:00
|
|
|
flags.backup || flags.holds ||
|
2020-01-10 21:16:58 +03:00
|
|
|
flags.saved || redactbook != NULL) {
|
2016-01-07 00:22:48 +03:00
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid flags combined with -t\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (argc > 0) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
2016-01-07 00:22:48 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("missing snapshot argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2020-01-10 21:16:58 +03:00
|
|
|
if (flags.saved) {
|
|
|
|
if (fromname != NULL || flags.replicate || flags.props ||
|
2020-04-23 20:06:57 +03:00
|
|
|
flags.doall || flags.backup ||
|
2020-01-10 21:16:58 +03:00
|
|
|
flags.holds || flags.largeblock || flags.embed_data ||
|
|
|
|
flags.compress || flags.raw || redactbook != NULL) {
|
|
|
|
(void) fprintf(stderr, gettext("incompatible flags "
|
|
|
|
"combined with saved send flag\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (strchr(argv[0], '@') != NULL) {
|
|
|
|
(void) fprintf(stderr, gettext("saved send must "
|
|
|
|
"specify the dataset with partially-received "
|
|
|
|
"state\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (flags.raw && redactbook != NULL) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Error: raw sends may not be redacted.\n"));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
if (!flags.dryrun && isatty(STDOUT_FILENO)) {
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Error: Stream can not be written to a terminal.\n"
|
|
|
|
"You must redirect standard output.\n"));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2020-01-10 21:16:58 +03:00
|
|
|
if (flags.saved) {
|
|
|
|
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
err = zfs_send_saved(zhp, &flags, STDOUT_FILENO,
|
|
|
|
resume_token);
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (err != 0);
|
|
|
|
} else if (resume_token != NULL) {
|
2016-01-07 00:22:48 +03:00
|
|
|
return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,
|
|
|
|
resume_token));
|
|
|
|
}
|
|
|
|
|
2013-12-12 02:33:41 +04:00
|
|
|
/*
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
* For everything except -R and -I, use the new, cleaner code path.
|
2013-12-12 02:33:41 +04:00
|
|
|
*/
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (!(flags.replicate || flags.doall)) {
|
2016-06-16 00:28:36 +03:00
|
|
|
char frombuf[ZFS_MAX_DATASET_NAME_LEN];
|
2013-12-12 02:33:41 +04:00
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (fromname != NULL && (strchr(fromname, '#') == NULL &&
|
|
|
|
strchr(fromname, '@') == NULL)) {
|
|
|
|
/*
|
|
|
|
* Neither bookmark or snapshot was specified. Print a
|
|
|
|
* warning, and assume snapshot.
|
|
|
|
*/
|
|
|
|
(void) fprintf(stderr, "Warning: incremental source "
|
|
|
|
"didn't specify type, assuming snapshot. Use '@' "
|
|
|
|
"or '#' prefix to avoid ambiguity.\n");
|
|
|
|
(void) snprintf(frombuf, sizeof (frombuf), "@%s",
|
|
|
|
fromname);
|
|
|
|
fromname = frombuf;
|
|
|
|
}
|
2013-12-12 02:33:41 +04:00
|
|
|
if (fromname != NULL &&
|
|
|
|
(fromname[0] == '#' || fromname[0] == '@')) {
|
|
|
|
/*
|
|
|
|
* Incremental source name begins with # or @.
|
|
|
|
* Default to same fs as target.
|
|
|
|
*/
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
char tmpbuf[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
(void) strlcpy(tmpbuf, fromname, sizeof (tmpbuf));
|
2016-09-23 01:55:41 +03:00
|
|
|
(void) strlcpy(frombuf, argv[0], sizeof (frombuf));
|
2013-12-12 02:33:41 +04:00
|
|
|
cp = strchr(frombuf, '@');
|
|
|
|
if (cp != NULL)
|
|
|
|
*cp = '\0';
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
(void) strlcat(frombuf, tmpbuf, sizeof (frombuf));
|
2013-12-12 02:33:41 +04:00
|
|
|
fromname = frombuf;
|
|
|
|
}
|
2020-03-18 22:54:12 +03:00
|
|
|
|
|
|
|
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (1);
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
err = zfs_send_one(zhp, fromname, STDOUT_FILENO, &flags,
|
|
|
|
redactbook);
|
2013-12-12 02:33:41 +04:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (err != 0);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2013-12-12 02:33:41 +04:00
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (fromname != NULL && strchr(fromname, '#')) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Error: multiple snapshots cannot be "
|
|
|
|
"sent from a bookmark.\n"));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redactbook != NULL) {
|
|
|
|
(void) fprintf(stderr, gettext("Error: multiple snapshots "
|
|
|
|
"cannot be sent redacted.\n"));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2019-07-09 01:10:23 +03:00
|
|
|
if ((cp = strchr(argv[0], '@')) == NULL) {
|
|
|
|
(void) fprintf(stderr, gettext("Error: "
|
|
|
|
"Unsupported flag with filesystem or bookmark.\n"));
|
|
|
|
return (1);
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
*cp = '\0';
|
|
|
|
toname = cp + 1;
|
|
|
|
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If they specified the full path to the snapshot, chop off
|
|
|
|
* everything except the short name of the snapshot, but special
|
|
|
|
* case if they specify the origin.
|
|
|
|
*/
|
|
|
|
if (fromname && (cp = strchr(fromname, '@')) != NULL) {
|
2016-06-16 00:28:36 +03:00
|
|
|
char origin[ZFS_MAX_DATASET_NAME_LEN];
|
2011-07-27 02:44:36 +04:00
|
|
|
zprop_source_t src;
|
|
|
|
|
|
|
|
(void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
|
|
|
|
origin, sizeof (origin), &src, NULL, 0, B_FALSE);
|
|
|
|
|
|
|
|
if (strcmp(origin, fromname) == 0) {
|
|
|
|
fromname = NULL;
|
|
|
|
flags.fromorigin = B_TRUE;
|
|
|
|
} else {
|
|
|
|
*cp = '\0';
|
|
|
|
if (cp != fromname && strcmp(argv[0], fromname)) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("incremental source must be "
|
|
|
|
"in same filesystem\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
fromname = cp + 1;
|
|
|
|
if (strchr(fromname, '@') || strchr(fromname, '/')) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid incremental source\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags.replicate && fromname == NULL)
|
|
|
|
flags.doall = B_TRUE;
|
|
|
|
|
2011-11-17 22:14:36 +04:00
|
|
|
err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
flags.verbosity >= 3 ? &dbgnv : NULL);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (flags.verbosity >= 3 && dbgnv != NULL) {
|
2011-07-27 02:44:36 +04:00
|
|
|
/*
|
|
|
|
* dump_nvlist prints to stdout, but that's been
|
|
|
|
* redirected to a file. Make it print to stderr
|
|
|
|
* instead.
|
|
|
|
*/
|
|
|
|
(void) dup2(STDERR_FILENO, STDOUT_FILENO);
|
|
|
|
dump_nvlist(dbgnv, 0);
|
|
|
|
nvlist_free(dbgnv);
|
|
|
|
}
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (err != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore a backup stream from stdin.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_receive(int argc, char **argv)
|
|
|
|
{
|
2017-01-18 01:45:02 +03:00
|
|
|
int c, err = 0;
|
2011-07-27 02:44:36 +04:00
|
|
|
recvflags_t flags = { 0 };
|
2016-01-07 00:22:48 +03:00
|
|
|
boolean_t abort_resumable = B_FALSE;
|
2015-12-22 04:31:57 +03:00
|
|
|
nvlist_t *props;
|
|
|
|
|
|
|
|
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
/* check options */
|
2020-03-17 20:08:32 +03:00
|
|
|
while ((c = getopt(argc, argv, ":o:x:dehMnuvFsA")) != -1) {
|
2011-07-27 02:44:36 +04:00
|
|
|
switch (c) {
|
2015-12-22 04:31:57 +03:00
|
|
|
case 'o':
|
2017-05-10 02:21:09 +03:00
|
|
|
if (!parseprop(props, optarg)) {
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(props);
|
2017-05-10 02:21:09 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
if (!parsepropname(props, optarg)) {
|
|
|
|
nvlist_free(props);
|
|
|
|
usage(B_FALSE);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
2015-12-22 04:31:57 +03:00
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'd':
|
2018-12-04 20:38:55 +03:00
|
|
|
if (flags.istail) {
|
|
|
|
(void) fprintf(stderr, gettext("invalid option "
|
|
|
|
"combination: -d and -e are mutually "
|
|
|
|
"exclusive\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
flags.isprefix = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'e':
|
2018-12-04 20:38:55 +03:00
|
|
|
if (flags.isprefix) {
|
|
|
|
(void) fprintf(stderr, gettext("invalid option "
|
|
|
|
"combination: -d and -e are mutually "
|
|
|
|
"exclusive\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
flags.istail = B_TRUE;
|
|
|
|
break;
|
2019-02-15 23:41:38 +03:00
|
|
|
case 'h':
|
|
|
|
flags.skipholds = B_TRUE;
|
|
|
|
break;
|
2020-03-17 20:08:32 +03:00
|
|
|
case 'M':
|
|
|
|
flags.forceunmount = B_TRUE;
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'n':
|
|
|
|
flags.dryrun = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
flags.nomount = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
flags.verbose = B_TRUE;
|
|
|
|
break;
|
2016-01-07 00:22:48 +03:00
|
|
|
case 's':
|
|
|
|
flags.resumable = B_TRUE;
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'F':
|
|
|
|
flags.force = B_TRUE;
|
|
|
|
break;
|
2016-01-07 00:22:48 +03:00
|
|
|
case 'A':
|
|
|
|
abort_resumable = B_TRUE;
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing argument for "
|
|
|
|
"'%c' option\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
2018-12-04 20:38:55 +03:00
|
|
|
/* zfs recv -e (use "tail" name) implies -d (remove dataset "head") */
|
|
|
|
if (flags.istail)
|
|
|
|
flags.isprefix = B_TRUE;
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2016-01-07 00:22:48 +03:00
|
|
|
if (abort_resumable) {
|
|
|
|
if (flags.isprefix || flags.istail || flags.dryrun ||
|
|
|
|
flags.resumable || flags.nomount) {
|
2017-06-09 19:51:13 +03:00
|
|
|
(void) fprintf(stderr, gettext("invalid option\n"));
|
2016-01-07 00:22:48 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2016-06-16 00:28:36 +03:00
|
|
|
char namebuf[ZFS_MAX_DATASET_NAME_LEN];
|
2016-01-07 00:22:48 +03:00
|
|
|
(void) snprintf(namebuf, sizeof (namebuf),
|
|
|
|
"%s/%%recv", argv[0]);
|
|
|
|
|
|
|
|
if (zfs_dataset_exists(g_zfs, namebuf,
|
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {
|
|
|
|
zfs_handle_t *zhp = zfs_open(g_zfs,
|
|
|
|
namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
2016-09-01 05:23:10 +03:00
|
|
|
if (zhp == NULL) {
|
|
|
|
nvlist_free(props);
|
2016-01-07 00:22:48 +03:00
|
|
|
return (1);
|
2016-09-01 05:23:10 +03:00
|
|
|
}
|
2016-01-07 00:22:48 +03:00
|
|
|
err = zfs_destroy(zhp, B_FALSE);
|
2016-09-01 05:23:10 +03:00
|
|
|
zfs_close(zhp);
|
2016-01-07 00:22:48 +03:00
|
|
|
} else {
|
|
|
|
zfs_handle_t *zhp = zfs_open(g_zfs,
|
|
|
|
argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL)
|
|
|
|
usage(B_FALSE);
|
|
|
|
if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||
|
|
|
|
zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
|
|
|
|
NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'%s' does not have any "
|
|
|
|
"resumable receive state to abort\n"),
|
|
|
|
argv[0]);
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(props);
|
|
|
|
zfs_close(zhp);
|
2016-01-07 00:22:48 +03:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
err = zfs_destroy(zhp, B_FALSE);
|
2016-09-01 05:23:10 +03:00
|
|
|
zfs_close(zhp);
|
2016-01-07 00:22:48 +03:00
|
|
|
}
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(props);
|
2016-01-07 00:22:48 +03:00
|
|
|
return (err != 0);
|
|
|
|
}
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
if (isatty(STDIN_FILENO)) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Error: Backup stream can not be read "
|
|
|
|
"from a terminal.\n"
|
|
|
|
"You must redirect standard input.\n"));
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(props);
|
2011-07-27 02:44:36 +04:00
|
|
|
return (1);
|
|
|
|
}
|
2015-12-22 04:31:57 +03:00
|
|
|
err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);
|
2016-09-01 05:23:10 +03:00
|
|
|
nvlist_free(props);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
return (err != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* allow/unallow stuff
|
|
|
|
*/
|
|
|
|
/* copied from zfs/sys/dsl_deleg.h */
|
|
|
|
#define ZFS_DELEG_PERM_CREATE "create"
|
|
|
|
#define ZFS_DELEG_PERM_DESTROY "destroy"
|
|
|
|
#define ZFS_DELEG_PERM_SNAPSHOT "snapshot"
|
|
|
|
#define ZFS_DELEG_PERM_ROLLBACK "rollback"
|
|
|
|
#define ZFS_DELEG_PERM_CLONE "clone"
|
|
|
|
#define ZFS_DELEG_PERM_PROMOTE "promote"
|
|
|
|
#define ZFS_DELEG_PERM_RENAME "rename"
|
|
|
|
#define ZFS_DELEG_PERM_MOUNT "mount"
|
|
|
|
#define ZFS_DELEG_PERM_SHARE "share"
|
|
|
|
#define ZFS_DELEG_PERM_SEND "send"
|
|
|
|
#define ZFS_DELEG_PERM_RECEIVE "receive"
|
|
|
|
#define ZFS_DELEG_PERM_ALLOW "allow"
|
|
|
|
#define ZFS_DELEG_PERM_USERPROP "userprop"
|
|
|
|
#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */
|
|
|
|
#define ZFS_DELEG_PERM_USERQUOTA "userquota"
|
|
|
|
#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"
|
|
|
|
#define ZFS_DELEG_PERM_USERUSED "userused"
|
|
|
|
#define ZFS_DELEG_PERM_GROUPUSED "groupused"
|
2016-10-04 21:46:10 +03:00
|
|
|
#define ZFS_DELEG_PERM_USEROBJQUOTA "userobjquota"
|
|
|
|
#define ZFS_DELEG_PERM_GROUPOBJQUOTA "groupobjquota"
|
|
|
|
#define ZFS_DELEG_PERM_USEROBJUSED "userobjused"
|
|
|
|
#define ZFS_DELEG_PERM_GROUPOBJUSED "groupobjused"
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
#define ZFS_DELEG_PERM_HOLD "hold"
|
|
|
|
#define ZFS_DELEG_PERM_RELEASE "release"
|
|
|
|
#define ZFS_DELEG_PERM_DIFF "diff"
|
2013-12-12 02:33:41 +04:00
|
|
|
#define ZFS_DELEG_PERM_BOOKMARK "bookmark"
|
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 20:36:48 +03:00
|
|
|
#define ZFS_DELEG_PERM_LOAD_KEY "load-key"
|
|
|
|
#define ZFS_DELEG_PERM_CHANGE_KEY "change-key"
|
2011-07-27 02:44:36 +04:00
|
|
|
|
2018-02-14 01:54:54 +03:00
|
|
|
#define ZFS_DELEG_PERM_PROJECTUSED "projectused"
|
|
|
|
#define ZFS_DELEG_PERM_PROJECTQUOTA "projectquota"
|
|
|
|
#define ZFS_DELEG_PERM_PROJECTOBJUSED "projectobjused"
|
|
|
|
#define ZFS_DELEG_PERM_PROJECTOBJQUOTA "projectobjquota"
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
|
|
|
|
|
|
|
|
static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
|
|
|
|
{ ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
|
|
|
|
{ ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
|
|
|
|
{ ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
|
|
|
|
{ ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
|
|
|
|
{ ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
|
|
|
|
{ ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
|
|
|
|
{ ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
|
|
|
|
{ ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
|
|
|
|
{ ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
|
|
|
|
{ ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
|
|
|
|
{ ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
|
|
|
|
{ ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
|
|
|
|
{ ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
|
|
|
|
{ ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
|
|
|
|
{ ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
|
2013-12-12 02:33:41 +04:00
|
|
|
{ ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
|
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 20:36:48 +03:00
|
|
|
{ ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY },
|
|
|
|
{ ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY },
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
{ ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
|
|
|
|
{ ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
|
|
|
|
{ ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
|
|
|
|
{ ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
|
|
|
|
{ ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
|
2016-10-04 21:46:10 +03:00
|
|
|
{ ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA },
|
|
|
|
{ ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED },
|
|
|
|
{ ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA },
|
|
|
|
{ ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED },
|
2018-02-14 01:54:54 +03:00
|
|
|
{ ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED },
|
|
|
|
{ ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA },
|
|
|
|
{ ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED },
|
|
|
|
{ ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA },
|
2011-07-27 02:44:36 +04:00
|
|
|
{ NULL, ZFS_DELEG_NOTE_NONE }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* permission structure */
|
|
|
|
typedef struct deleg_perm {
|
|
|
|
zfs_deleg_who_type_t dp_who_type;
|
|
|
|
const char *dp_name;
|
|
|
|
boolean_t dp_local;
|
|
|
|
boolean_t dp_descend;
|
|
|
|
} deleg_perm_t;
|
|
|
|
|
|
|
|
/* */
|
|
|
|
typedef struct deleg_perm_node {
|
|
|
|
deleg_perm_t dpn_perm;
|
|
|
|
|
|
|
|
uu_avl_node_t dpn_avl_node;
|
|
|
|
} deleg_perm_node_t;
|
|
|
|
|
|
|
|
typedef struct fs_perm fs_perm_t;
|
|
|
|
|
|
|
|
/* permissions set */
|
|
|
|
typedef struct who_perm {
|
|
|
|
zfs_deleg_who_type_t who_type;
|
|
|
|
const char *who_name; /* id */
|
|
|
|
char who_ug_name[256]; /* user/group name */
|
|
|
|
fs_perm_t *who_fsperm; /* uplink */
|
|
|
|
|
|
|
|
uu_avl_t *who_deleg_perm_avl; /* permissions */
|
|
|
|
} who_perm_t;
|
|
|
|
|
|
|
|
/* */
|
|
|
|
typedef struct who_perm_node {
|
|
|
|
who_perm_t who_perm;
|
|
|
|
uu_avl_node_t who_avl_node;
|
|
|
|
} who_perm_node_t;
|
|
|
|
|
|
|
|
typedef struct fs_perm_set fs_perm_set_t;
|
|
|
|
/* fs permissions */
|
|
|
|
struct fs_perm {
|
|
|
|
const char *fsp_name;
|
|
|
|
|
|
|
|
uu_avl_t *fsp_sc_avl; /* sets,create */
|
|
|
|
uu_avl_t *fsp_uge_avl; /* user,group,everyone */
|
|
|
|
|
|
|
|
fs_perm_set_t *fsp_set; /* uplink */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* */
|
|
|
|
typedef struct fs_perm_node {
|
|
|
|
fs_perm_t fspn_fsperm;
|
|
|
|
uu_avl_t *fspn_avl;
|
|
|
|
|
|
|
|
uu_list_node_t fspn_list_node;
|
|
|
|
} fs_perm_node_t;
|
|
|
|
|
|
|
|
/* top level structure */
|
|
|
|
struct fs_perm_set {
|
|
|
|
uu_list_pool_t *fsps_list_pool;
|
|
|
|
uu_list_t *fsps_list; /* list of fs_perms */
|
|
|
|
|
|
|
|
uu_avl_pool_t *fsps_named_set_avl_pool;
|
|
|
|
uu_avl_pool_t *fsps_who_perm_avl_pool;
|
|
|
|
uu_avl_pool_t *fsps_deleg_perm_avl_pool;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline const char *
|
|
|
|
deleg_perm_type(zfs_deleg_note_t note)
|
|
|
|
{
|
|
|
|
/* subcommands */
|
|
|
|
switch (note) {
|
|
|
|
/* SUBCOMMANDS */
|
|
|
|
/* OTHER */
|
|
|
|
case ZFS_DELEG_NOTE_GROUPQUOTA:
|
|
|
|
case ZFS_DELEG_NOTE_GROUPUSED:
|
|
|
|
case ZFS_DELEG_NOTE_USERPROP:
|
|
|
|
case ZFS_DELEG_NOTE_USERQUOTA:
|
|
|
|
case ZFS_DELEG_NOTE_USERUSED:
|
2016-10-04 21:46:10 +03:00
|
|
|
case ZFS_DELEG_NOTE_USEROBJQUOTA:
|
|
|
|
case ZFS_DELEG_NOTE_USEROBJUSED:
|
|
|
|
case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
|
|
|
|
case ZFS_DELEG_NOTE_GROUPOBJUSED:
|
2018-02-14 01:54:54 +03:00
|
|
|
case ZFS_DELEG_NOTE_PROJECTUSED:
|
|
|
|
case ZFS_DELEG_NOTE_PROJECTQUOTA:
|
|
|
|
case ZFS_DELEG_NOTE_PROJECTOBJUSED:
|
|
|
|
case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
|
2011-07-27 02:44:36 +04:00
|
|
|
/* other */
|
|
|
|
return (gettext("other"));
|
|
|
|
default:
|
|
|
|
return (gettext("subcommand"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 01:45:02 +03:00
|
|
|
static int
|
2011-07-27 02:44:36 +04:00
|
|
|
who_type2weight(zfs_deleg_who_type_t who_type)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
switch (who_type) {
|
|
|
|
case ZFS_DELEG_NAMED_SET_SETS:
|
|
|
|
case ZFS_DELEG_NAMED_SET:
|
|
|
|
res = 0;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_CREATE_SETS:
|
|
|
|
case ZFS_DELEG_CREATE:
|
|
|
|
res = 1;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_USER_SETS:
|
|
|
|
case ZFS_DELEG_USER:
|
|
|
|
res = 2;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_GROUP_SETS:
|
|
|
|
case ZFS_DELEG_GROUP:
|
|
|
|
res = 3;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_EVERYONE_SETS:
|
|
|
|
case ZFS_DELEG_EVERYONE:
|
|
|
|
res = 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
who_perm_compare(const void *larg, const void *rarg, void *unused)
|
|
|
|
{
|
|
|
|
const who_perm_node_t *l = larg;
|
|
|
|
const who_perm_node_t *r = rarg;
|
|
|
|
zfs_deleg_who_type_t ltype = l->who_perm.who_type;
|
|
|
|
zfs_deleg_who_type_t rtype = r->who_perm.who_type;
|
|
|
|
int lweight = who_type2weight(ltype);
|
|
|
|
int rweight = who_type2weight(rtype);
|
|
|
|
int res = lweight - rweight;
|
|
|
|
if (res == 0)
|
|
|
|
res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
|
|
|
|
ZFS_MAX_DELEG_NAME-1);
|
|
|
|
|
|
|
|
if (res == 0)
|
|
|
|
return (0);
|
|
|
|
if (res > 0)
|
|
|
|
return (1);
|
|
|
|
else
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
deleg_perm_compare(const void *larg, const void *rarg, void *unused)
|
|
|
|
{
|
|
|
|
const deleg_perm_node_t *l = larg;
|
|
|
|
const deleg_perm_node_t *r = rarg;
|
|
|
|
int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
|
|
|
|
ZFS_MAX_DELEG_NAME-1);
|
|
|
|
|
|
|
|
if (res == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if (res > 0)
|
|
|
|
return (1);
|
|
|
|
else
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
fs_perm_set_init(fs_perm_set_t *fspset)
|
|
|
|
{
|
|
|
|
bzero(fspset, sizeof (fs_perm_set_t));
|
|
|
|
|
|
|
|
if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
|
|
|
|
sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
|
|
|
|
NULL, UU_DEFAULT)) == NULL)
|
|
|
|
nomem();
|
|
|
|
if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
|
|
|
|
UU_DEFAULT)) == NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
|
|
|
|
"named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
|
|
|
|
who_perm_node_t, who_avl_node), who_perm_compare,
|
|
|
|
UU_DEFAULT)) == NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
|
|
|
|
"who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
|
|
|
|
who_perm_node_t, who_avl_node), who_perm_compare,
|
|
|
|
UU_DEFAULT)) == NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
|
|
|
|
"deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
|
|
|
|
deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
|
|
|
|
== NULL)
|
|
|
|
nomem();
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void fs_perm_fini(fs_perm_t *);
|
|
|
|
static inline void who_perm_fini(who_perm_t *);
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
fs_perm_set_fini(fs_perm_set_t *fspset)
|
|
|
|
{
|
|
|
|
fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
|
|
|
|
|
|
|
|
while (node != NULL) {
|
|
|
|
fs_perm_node_t *next_node =
|
|
|
|
uu_list_next(fspset->fsps_list, node);
|
|
|
|
fs_perm_t *fsperm = &node->fspn_fsperm;
|
|
|
|
fs_perm_fini(fsperm);
|
|
|
|
uu_list_remove(fspset->fsps_list, node);
|
|
|
|
free(node);
|
|
|
|
node = next_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
|
|
|
|
uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
|
|
|
|
uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
deleg_perm->dp_who_type = type;
|
|
|
|
deleg_perm->dp_name = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
|
|
|
|
zfs_deleg_who_type_t type, const char *name)
|
|
|
|
{
|
|
|
|
uu_avl_pool_t *pool;
|
|
|
|
pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
|
|
|
|
|
|
|
|
bzero(who_perm, sizeof (who_perm_t));
|
|
|
|
|
|
|
|
if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
|
|
|
|
UU_DEFAULT)) == NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
who_perm->who_type = type;
|
|
|
|
who_perm->who_name = name;
|
|
|
|
who_perm->who_fsperm = fsperm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
who_perm_fini(who_perm_t *who_perm)
|
|
|
|
{
|
|
|
|
deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
|
|
|
|
|
|
|
|
while (node != NULL) {
|
|
|
|
deleg_perm_node_t *next_node =
|
|
|
|
uu_avl_next(who_perm->who_deleg_perm_avl, node);
|
|
|
|
|
|
|
|
uu_avl_remove(who_perm->who_deleg_perm_avl, node);
|
|
|
|
free(node);
|
|
|
|
node = next_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
uu_avl_destroy(who_perm->who_deleg_perm_avl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
|
|
|
|
{
|
|
|
|
uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool;
|
|
|
|
uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool;
|
|
|
|
|
|
|
|
bzero(fsperm, sizeof (fs_perm_t));
|
|
|
|
|
|
|
|
if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
|
|
|
|
== NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
|
|
|
|
== NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
fsperm->fsp_set = fspset;
|
|
|
|
fsperm->fsp_name = fsname;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
fs_perm_fini(fs_perm_t *fsperm)
|
|
|
|
{
|
|
|
|
who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
|
|
|
|
while (node != NULL) {
|
|
|
|
who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
|
|
|
|
node);
|
|
|
|
who_perm_t *who_perm = &node->who_perm;
|
|
|
|
who_perm_fini(who_perm);
|
|
|
|
uu_avl_remove(fsperm->fsp_sc_avl, node);
|
|
|
|
free(node);
|
|
|
|
node = next_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = uu_avl_first(fsperm->fsp_uge_avl);
|
|
|
|
while (node != NULL) {
|
|
|
|
who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
|
|
|
|
node);
|
|
|
|
who_perm_t *who_perm = &node->who_perm;
|
|
|
|
who_perm_fini(who_perm);
|
|
|
|
uu_avl_remove(fsperm->fsp_uge_avl, node);
|
|
|
|
free(node);
|
|
|
|
node = next_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
uu_avl_destroy(fsperm->fsp_sc_avl);
|
|
|
|
uu_avl_destroy(fsperm->fsp_uge_avl);
|
|
|
|
}
|
|
|
|
|
2017-01-18 01:45:02 +03:00
|
|
|
static void
|
2011-07-27 02:44:36 +04:00
|
|
|
set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
|
|
|
|
zfs_deleg_who_type_t who_type, const char *name, char locality)
|
|
|
|
{
|
|
|
|
uu_avl_index_t idx = 0;
|
|
|
|
|
|
|
|
deleg_perm_node_t *found_node = NULL;
|
|
|
|
deleg_perm_t *deleg_perm = &node->dpn_perm;
|
|
|
|
|
|
|
|
deleg_perm_init(deleg_perm, who_type, name);
|
|
|
|
|
|
|
|
if ((found_node = uu_avl_find(avl, node, NULL, &idx))
|
|
|
|
== NULL)
|
|
|
|
uu_avl_insert(avl, node, idx);
|
|
|
|
else {
|
|
|
|
node = found_node;
|
|
|
|
deleg_perm = &node->dpn_perm;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (locality) {
|
|
|
|
case ZFS_DELEG_LOCAL:
|
|
|
|
deleg_perm->dp_local = B_TRUE;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_DESCENDENT:
|
|
|
|
deleg_perm->dp_descend = B_TRUE;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NA:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(B_FALSE); /* invalid locality */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
|
|
|
|
{
|
|
|
|
nvpair_t *nvp = NULL;
|
|
|
|
fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
|
|
|
|
uu_avl_t *avl = who_perm->who_deleg_perm_avl;
|
|
|
|
zfs_deleg_who_type_t who_type = who_perm->who_type;
|
|
|
|
|
|
|
|
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
|
|
|
|
const char *name = nvpair_name(nvp);
|
|
|
|
data_type_t type = nvpair_type(nvp);
|
|
|
|
uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
|
|
|
|
deleg_perm_node_t *node =
|
|
|
|
safe_malloc(sizeof (deleg_perm_node_t));
|
|
|
|
|
|
|
|
VERIFY(type == DATA_TYPE_BOOLEAN);
|
|
|
|
|
|
|
|
uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
|
|
|
|
set_deleg_perm_node(avl, node, who_type, name, locality);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
|
|
|
|
{
|
|
|
|
nvpair_t *nvp = NULL;
|
|
|
|
fs_perm_set_t *fspset = fsperm->fsp_set;
|
|
|
|
|
|
|
|
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
|
|
|
|
nvlist_t *nvl2 = NULL;
|
|
|
|
const char *name = nvpair_name(nvp);
|
|
|
|
uu_avl_t *avl = NULL;
|
|
|
|
uu_avl_pool_t *avl_pool = NULL;
|
|
|
|
zfs_deleg_who_type_t perm_type = name[0];
|
|
|
|
char perm_locality = name[1];
|
|
|
|
const char *perm_name = name + 3;
|
|
|
|
who_perm_t *who_perm = NULL;
|
|
|
|
|
|
|
|
assert('$' == name[2]);
|
|
|
|
|
|
|
|
if (nvpair_value_nvlist(nvp, &nvl2) != 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
switch (perm_type) {
|
|
|
|
case ZFS_DELEG_CREATE:
|
|
|
|
case ZFS_DELEG_CREATE_SETS:
|
|
|
|
case ZFS_DELEG_NAMED_SET:
|
|
|
|
case ZFS_DELEG_NAMED_SET_SETS:
|
|
|
|
avl_pool = fspset->fsps_named_set_avl_pool;
|
|
|
|
avl = fsperm->fsp_sc_avl;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_USER:
|
|
|
|
case ZFS_DELEG_USER_SETS:
|
|
|
|
case ZFS_DELEG_GROUP:
|
|
|
|
case ZFS_DELEG_GROUP_SETS:
|
|
|
|
case ZFS_DELEG_EVERYONE:
|
|
|
|
case ZFS_DELEG_EVERYONE_SETS:
|
|
|
|
avl_pool = fspset->fsps_who_perm_avl_pool;
|
|
|
|
avl = fsperm->fsp_uge_avl;
|
|
|
|
break;
|
2017-01-18 01:45:02 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
default:
|
2017-01-18 01:45:02 +03:00
|
|
|
assert(!"unhandled zfs_deleg_who_type_t");
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
2019-12-17 02:49:28 +03:00
|
|
|
who_perm_node_t *found_node = NULL;
|
|
|
|
who_perm_node_t *node = safe_malloc(
|
|
|
|
sizeof (who_perm_node_t));
|
|
|
|
who_perm = &node->who_perm;
|
|
|
|
uu_avl_index_t idx = 0;
|
|
|
|
|
|
|
|
uu_avl_node_init(node, &node->who_avl_node, avl_pool);
|
|
|
|
who_perm_init(who_perm, fsperm, perm_type, perm_name);
|
|
|
|
|
|
|
|
if ((found_node = uu_avl_find(avl, node, NULL, &idx))
|
|
|
|
== NULL) {
|
|
|
|
if (avl == fsperm->fsp_uge_avl) {
|
|
|
|
uid_t rid = 0;
|
|
|
|
struct passwd *p = NULL;
|
|
|
|
struct group *g = NULL;
|
|
|
|
const char *nice_name = NULL;
|
|
|
|
|
|
|
|
switch (perm_type) {
|
|
|
|
case ZFS_DELEG_USER_SETS:
|
|
|
|
case ZFS_DELEG_USER:
|
|
|
|
rid = atoi(perm_name);
|
|
|
|
p = getpwuid(rid);
|
|
|
|
if (p)
|
|
|
|
nice_name = p->pw_name;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_GROUP_SETS:
|
|
|
|
case ZFS_DELEG_GROUP:
|
|
|
|
rid = atoi(perm_name);
|
|
|
|
g = getgrgid(rid);
|
|
|
|
if (g)
|
|
|
|
nice_name = g->gr_name;
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
|
2019-12-17 02:49:28 +03:00
|
|
|
default:
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
2019-12-17 02:49:28 +03:00
|
|
|
if (nice_name != NULL)
|
|
|
|
(void) strlcpy(
|
|
|
|
node->who_perm.who_ug_name,
|
|
|
|
nice_name, 256);
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
2019-12-17 02:49:28 +03:00
|
|
|
|
|
|
|
uu_avl_insert(avl, node, idx);
|
|
|
|
} else {
|
|
|
|
node = found_node;
|
|
|
|
who_perm = &node->who_perm;
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
2019-12-17 02:49:28 +03:00
|
|
|
|
|
|
|
assert(who_perm != NULL);
|
2011-07-27 02:44:36 +04:00
|
|
|
(void) parse_who_perm(who_perm, nvl2, perm_locality);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
|
|
|
|
{
|
|
|
|
nvpair_t *nvp = NULL;
|
|
|
|
uu_avl_index_t idx = 0;
|
|
|
|
|
|
|
|
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
|
|
|
|
nvlist_t *nvl2 = NULL;
|
|
|
|
const char *fsname = nvpair_name(nvp);
|
|
|
|
data_type_t type = nvpair_type(nvp);
|
|
|
|
fs_perm_t *fsperm = NULL;
|
|
|
|
fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
|
|
|
|
if (node == NULL)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
fsperm = &node->fspn_fsperm;
|
|
|
|
|
|
|
|
VERIFY(DATA_TYPE_NVLIST == type);
|
|
|
|
|
|
|
|
uu_list_node_init(node, &node->fspn_list_node,
|
|
|
|
fspset->fsps_list_pool);
|
|
|
|
|
|
|
|
idx = uu_list_numnodes(fspset->fsps_list);
|
|
|
|
fs_perm_init(fsperm, fspset, fsname);
|
|
|
|
|
|
|
|
if (nvpair_value_nvlist(nvp, &nvl2) != 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
(void) parse_fs_perm(fsperm, nvl2);
|
|
|
|
|
|
|
|
uu_list_insert(fspset->fsps_list, node, idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const char *
|
|
|
|
deleg_perm_comment(zfs_deleg_note_t note)
|
|
|
|
{
|
|
|
|
const char *str = "";
|
|
|
|
|
|
|
|
/* subcommands */
|
|
|
|
switch (note) {
|
|
|
|
/* SUBCOMMANDS */
|
|
|
|
case ZFS_DELEG_NOTE_ALLOW:
|
|
|
|
str = gettext("Must also have the permission that is being"
|
|
|
|
"\n\t\t\t\tallowed");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_CLONE:
|
|
|
|
str = gettext("Must also have the 'create' ability and 'mount'"
|
|
|
|
"\n\t\t\t\tability in the origin file system");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_CREATE:
|
|
|
|
str = gettext("Must also have the 'mount' ability");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_DESTROY:
|
|
|
|
str = gettext("Must also have the 'mount' ability");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_DIFF:
|
|
|
|
str = gettext("Allows lookup of paths within a dataset;"
|
|
|
|
"\n\t\t\t\tgiven an object number. Ordinary users need this"
|
|
|
|
"\n\t\t\t\tin order to use zfs diff");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_HOLD:
|
|
|
|
str = gettext("Allows adding a user hold to a snapshot");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_MOUNT:
|
|
|
|
str = gettext("Allows mount/umount of ZFS datasets");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_PROMOTE:
|
|
|
|
str = gettext("Must also have the 'mount'\n\t\t\t\tand"
|
|
|
|
" 'promote' ability in the origin file system");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_RECEIVE:
|
|
|
|
str = gettext("Must also have the 'mount' and 'create'"
|
|
|
|
" ability");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_RELEASE:
|
|
|
|
str = gettext("Allows releasing a user hold which\n\t\t\t\t"
|
|
|
|
"might destroy the snapshot");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_RENAME:
|
|
|
|
str = gettext("Must also have the 'mount' and 'create'"
|
|
|
|
"\n\t\t\t\tability in the new parent");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_ROLLBACK:
|
|
|
|
str = gettext("");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_SEND:
|
|
|
|
str = gettext("");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_SHARE:
|
|
|
|
str = gettext("Allows sharing file systems over NFS or SMB"
|
|
|
|
"\n\t\t\t\tprotocols");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_SNAPSHOT:
|
|
|
|
str = gettext("");
|
|
|
|
break;
|
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 20:36:48 +03:00
|
|
|
case ZFS_DELEG_NOTE_LOAD_KEY:
|
|
|
|
str = gettext("Allows loading or unloading an encryption key");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_CHANGE_KEY:
|
|
|
|
str = gettext("Allows changing or adding an encryption key");
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
/*
|
|
|
|
* case ZFS_DELEG_NOTE_VSCAN:
|
|
|
|
* str = gettext("");
|
|
|
|
* break;
|
|
|
|
*/
|
|
|
|
/* OTHER */
|
|
|
|
case ZFS_DELEG_NOTE_GROUPQUOTA:
|
|
|
|
str = gettext("Allows accessing any groupquota@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_GROUPUSED:
|
|
|
|
str = gettext("Allows reading any groupused@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_USERPROP:
|
|
|
|
str = gettext("Allows changing any user property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_USERQUOTA:
|
|
|
|
str = gettext("Allows accessing any userquota@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_USERUSED:
|
|
|
|
str = gettext("Allows reading any userused@... property");
|
|
|
|
break;
|
2016-10-04 21:46:10 +03:00
|
|
|
case ZFS_DELEG_NOTE_USEROBJQUOTA:
|
|
|
|
str = gettext("Allows accessing any userobjquota@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
|
|
|
|
str = gettext("Allows accessing any \n\t\t\t\t"
|
|
|
|
"groupobjquota@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_GROUPOBJUSED:
|
|
|
|
str = gettext("Allows reading any groupobjused@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_USEROBJUSED:
|
|
|
|
str = gettext("Allows reading any userobjused@... property");
|
|
|
|
break;
|
2018-02-14 01:54:54 +03:00
|
|
|
case ZFS_DELEG_NOTE_PROJECTQUOTA:
|
|
|
|
str = gettext("Allows accessing any projectquota@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
|
|
|
|
str = gettext("Allows accessing any \n\t\t\t\t"
|
|
|
|
"projectobjquota@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_PROJECTUSED:
|
|
|
|
str = gettext("Allows reading any projectused@... property");
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_NOTE_PROJECTOBJUSED:
|
|
|
|
str = gettext("Allows accessing any \n\t\t\t\t"
|
|
|
|
"projectobjused@... property");
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
/* other */
|
|
|
|
default:
|
|
|
|
str = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return (str);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct allow_opts {
|
|
|
|
boolean_t local;
|
|
|
|
boolean_t descend;
|
|
|
|
boolean_t user;
|
|
|
|
boolean_t group;
|
|
|
|
boolean_t everyone;
|
|
|
|
boolean_t create;
|
|
|
|
boolean_t set;
|
|
|
|
boolean_t recursive; /* unallow only */
|
|
|
|
boolean_t prt_usage;
|
|
|
|
|
|
|
|
boolean_t prt_perms;
|
|
|
|
char *who;
|
|
|
|
char *perms;
|
|
|
|
const char *dataset;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
prop_cmp(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const char *str1 = *(const char **)a;
|
|
|
|
const char *str2 = *(const char **)b;
|
|
|
|
return (strcmp(str1, str2));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
allow_usage(boolean_t un, boolean_t requested, const char *msg)
|
|
|
|
{
|
|
|
|
const char *opt_desc[] = {
|
|
|
|
"-h", gettext("show this help message and exit"),
|
|
|
|
"-l", gettext("set permission locally"),
|
|
|
|
"-d", gettext("set permission for descents"),
|
|
|
|
"-u", gettext("set permission for user"),
|
|
|
|
"-g", gettext("set permission for group"),
|
|
|
|
"-e", gettext("set permission for everyone"),
|
|
|
|
"-c", gettext("set create time permission"),
|
|
|
|
"-s", gettext("define permission set"),
|
|
|
|
/* unallow only */
|
|
|
|
"-r", gettext("remove permissions recursively"),
|
|
|
|
};
|
|
|
|
size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
|
|
|
|
size_t allow_size = unallow_size - 2;
|
|
|
|
const char *props[ZFS_NUM_PROPS];
|
|
|
|
int i;
|
|
|
|
size_t count = 0;
|
|
|
|
FILE *fp = requested ? stdout : stderr;
|
|
|
|
zprop_desc_t *pdtbl = zfs_prop_get_table();
|
|
|
|
const char *fmt = gettext("%-16s %-14s\t%s\n");
|
|
|
|
|
|
|
|
(void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
|
|
|
|
HELP_ALLOW));
|
|
|
|
(void) fprintf(fp, gettext("Options:\n"));
|
2016-11-09 04:33:23 +03:00
|
|
|
for (i = 0; i < (un ? unallow_size : allow_size); i += 2) {
|
|
|
|
const char *opt = opt_desc[i];
|
|
|
|
const char *optdsc = opt_desc[i + 1];
|
2011-07-27 02:44:36 +04:00
|
|
|
(void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc);
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) fprintf(fp, gettext("\nThe following permissions are "
|
|
|
|
"supported:\n\n"));
|
|
|
|
(void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
|
|
|
|
gettext("NOTES"));
|
|
|
|
for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
|
|
|
|
const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
|
|
|
|
zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
|
|
|
|
const char *perm_type = deleg_perm_type(perm_note);
|
|
|
|
const char *perm_comment = deleg_perm_comment(perm_note);
|
|
|
|
(void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ZFS_NUM_PROPS; i++) {
|
|
|
|
zprop_desc_t *pd = &pdtbl[i];
|
|
|
|
if (pd->pd_visible != B_TRUE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pd->pd_attr == PROP_READONLY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
props[count++] = pd->pd_name;
|
|
|
|
}
|
|
|
|
props[count] = NULL;
|
|
|
|
|
|
|
|
qsort(props, count, sizeof (char *), prop_cmp);
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
(void) fprintf(fp, fmt, props[i], gettext("property"), "");
|
|
|
|
|
|
|
|
if (msg != NULL)
|
|
|
|
(void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
|
|
|
|
|
|
|
|
exit(requested ? 0 : 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline const char *
|
|
|
|
munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
|
|
|
|
char **permsp)
|
|
|
|
{
|
|
|
|
if (un && argc == expected_argc - 1)
|
|
|
|
*permsp = NULL;
|
|
|
|
else if (argc == expected_argc)
|
|
|
|
*permsp = argv[argc - 2];
|
|
|
|
else
|
|
|
|
allow_usage(un, B_FALSE,
|
|
|
|
gettext("wrong number of parameters\n"));
|
|
|
|
|
|
|
|
return (argv[argc - 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
|
|
|
|
{
|
|
|
|
int uge_sum = opts->user + opts->group + opts->everyone;
|
|
|
|
int csuge_sum = opts->create + opts->set + uge_sum;
|
|
|
|
int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
|
|
|
|
int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
|
|
|
|
|
|
|
|
if (uge_sum > 1)
|
|
|
|
allow_usage(un, B_FALSE,
|
|
|
|
gettext("-u, -g, and -e are mutually exclusive\n"));
|
|
|
|
|
|
|
|
if (opts->prt_usage) {
|
|
|
|
if (argc == 0 && all_sum == 0)
|
|
|
|
allow_usage(un, B_TRUE, NULL);
|
|
|
|
else
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts->set) {
|
|
|
|
if (csuge_sum > 1)
|
|
|
|
allow_usage(un, B_FALSE,
|
|
|
|
gettext("invalid options combined with -s\n"));
|
|
|
|
|
|
|
|
opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
|
|
|
|
if (argv[0][0] != '@')
|
|
|
|
allow_usage(un, B_FALSE,
|
|
|
|
gettext("invalid set name: missing '@' prefix\n"));
|
|
|
|
opts->who = argv[0];
|
|
|
|
} else if (opts->create) {
|
|
|
|
if (ldcsuge_sum > 1)
|
|
|
|
allow_usage(un, B_FALSE,
|
|
|
|
gettext("invalid options combined with -c\n"));
|
|
|
|
opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
|
|
|
|
} else if (opts->everyone) {
|
|
|
|
if (csuge_sum > 1)
|
|
|
|
allow_usage(un, B_FALSE,
|
|
|
|
gettext("invalid options combined with -e\n"));
|
|
|
|
opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
|
|
|
|
} else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
|
|
|
|
== 0) {
|
|
|
|
opts->everyone = B_TRUE;
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
|
2012-01-16 17:07:04 +04:00
|
|
|
} else if (argc == 1 && !un) {
|
2011-07-27 02:44:36 +04:00
|
|
|
opts->prt_perms = B_TRUE;
|
|
|
|
opts->dataset = argv[argc-1];
|
|
|
|
} else {
|
|
|
|
opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
|
|
|
|
opts->who = argv[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!opts->local && !opts->descend) {
|
|
|
|
opts->local = B_TRUE;
|
|
|
|
opts->descend = B_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
|
|
|
|
const char *who, char *perms, nvlist_t *top_nvl)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char ld[2] = { '\0', '\0' };
|
2016-06-16 00:28:36 +03:00
|
|
|
char who_buf[MAXNAMELEN + 32];
|
2017-01-18 01:45:02 +03:00
|
|
|
char base_type = '\0';
|
|
|
|
char set_type = '\0';
|
2011-07-27 02:44:36 +04:00
|
|
|
nvlist_t *base_nvl = NULL;
|
|
|
|
nvlist_t *set_nvl = NULL;
|
|
|
|
nvlist_t *nvl;
|
|
|
|
|
|
|
|
if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
|
|
|
if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case ZFS_DELEG_NAMED_SET_SETS:
|
|
|
|
case ZFS_DELEG_NAMED_SET:
|
|
|
|
set_type = ZFS_DELEG_NAMED_SET_SETS;
|
|
|
|
base_type = ZFS_DELEG_NAMED_SET;
|
|
|
|
ld[0] = ZFS_DELEG_NA;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_CREATE_SETS:
|
|
|
|
case ZFS_DELEG_CREATE:
|
|
|
|
set_type = ZFS_DELEG_CREATE_SETS;
|
|
|
|
base_type = ZFS_DELEG_CREATE;
|
|
|
|
ld[0] = ZFS_DELEG_NA;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_USER_SETS:
|
|
|
|
case ZFS_DELEG_USER:
|
|
|
|
set_type = ZFS_DELEG_USER_SETS;
|
|
|
|
base_type = ZFS_DELEG_USER;
|
|
|
|
if (local)
|
|
|
|
ld[0] = ZFS_DELEG_LOCAL;
|
|
|
|
if (descend)
|
|
|
|
ld[1] = ZFS_DELEG_DESCENDENT;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_GROUP_SETS:
|
|
|
|
case ZFS_DELEG_GROUP:
|
|
|
|
set_type = ZFS_DELEG_GROUP_SETS;
|
|
|
|
base_type = ZFS_DELEG_GROUP;
|
|
|
|
if (local)
|
|
|
|
ld[0] = ZFS_DELEG_LOCAL;
|
|
|
|
if (descend)
|
|
|
|
ld[1] = ZFS_DELEG_DESCENDENT;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_EVERYONE_SETS:
|
|
|
|
case ZFS_DELEG_EVERYONE:
|
|
|
|
set_type = ZFS_DELEG_EVERYONE_SETS;
|
|
|
|
base_type = ZFS_DELEG_EVERYONE;
|
|
|
|
if (local)
|
|
|
|
ld[0] = ZFS_DELEG_LOCAL;
|
|
|
|
if (descend)
|
|
|
|
ld[1] = ZFS_DELEG_DESCENDENT;
|
|
|
|
break;
|
2017-01-18 01:45:02 +03:00
|
|
|
|
|
|
|
default:
|
|
|
|
assert(set_type != '\0' && base_type != '\0');
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (perms != NULL) {
|
|
|
|
char *curr = perms;
|
|
|
|
char *end = curr + strlen(perms);
|
|
|
|
|
|
|
|
while (curr < end) {
|
|
|
|
char *delim = strchr(curr, ',');
|
|
|
|
if (delim == NULL)
|
|
|
|
delim = end;
|
|
|
|
else
|
|
|
|
*delim = '\0';
|
|
|
|
|
|
|
|
if (curr[0] == '@')
|
|
|
|
nvl = set_nvl;
|
|
|
|
else
|
|
|
|
nvl = base_nvl;
|
|
|
|
|
|
|
|
(void) nvlist_add_boolean(nvl, curr);
|
|
|
|
if (delim != end)
|
|
|
|
*delim = ',';
|
|
|
|
curr = delim + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
char locality = ld[i];
|
|
|
|
if (locality == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!nvlist_empty(base_nvl)) {
|
|
|
|
if (who != NULL)
|
|
|
|
(void) snprintf(who_buf,
|
|
|
|
sizeof (who_buf), "%c%c$%s",
|
|
|
|
base_type, locality, who);
|
|
|
|
else
|
|
|
|
(void) snprintf(who_buf,
|
|
|
|
sizeof (who_buf), "%c%c$",
|
|
|
|
base_type, locality);
|
|
|
|
|
|
|
|
(void) nvlist_add_nvlist(top_nvl, who_buf,
|
|
|
|
base_nvl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!nvlist_empty(set_nvl)) {
|
|
|
|
if (who != NULL)
|
|
|
|
(void) snprintf(who_buf,
|
|
|
|
sizeof (who_buf), "%c%c$%s",
|
|
|
|
set_type, locality, who);
|
|
|
|
else
|
|
|
|
(void) snprintf(who_buf,
|
|
|
|
sizeof (who_buf), "%c%c$",
|
|
|
|
set_type, locality);
|
|
|
|
|
|
|
|
(void) nvlist_add_nvlist(top_nvl, who_buf,
|
|
|
|
set_nvl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
char locality = ld[i];
|
|
|
|
if (locality == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (who != NULL)
|
|
|
|
(void) snprintf(who_buf, sizeof (who_buf),
|
|
|
|
"%c%c$%s", base_type, locality, who);
|
|
|
|
else
|
|
|
|
(void) snprintf(who_buf, sizeof (who_buf),
|
|
|
|
"%c%c$", base_type, locality);
|
|
|
|
(void) nvlist_add_boolean(top_nvl, who_buf);
|
|
|
|
|
|
|
|
if (who != NULL)
|
|
|
|
(void) snprintf(who_buf, sizeof (who_buf),
|
|
|
|
"%c%c$%s", set_type, locality, who);
|
|
|
|
else
|
|
|
|
(void) snprintf(who_buf, sizeof (who_buf),
|
|
|
|
"%c%c$", set_type, locality);
|
|
|
|
(void) nvlist_add_boolean(top_nvl, who_buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
|
|
|
|
{
|
|
|
|
if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
if (opts->set) {
|
|
|
|
store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
|
|
|
|
opts->descend, opts->who, opts->perms, *nvlp);
|
|
|
|
} else if (opts->create) {
|
|
|
|
store_allow_perm(ZFS_DELEG_CREATE, opts->local,
|
|
|
|
opts->descend, NULL, opts->perms, *nvlp);
|
|
|
|
} else if (opts->everyone) {
|
|
|
|
store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
|
|
|
|
opts->descend, NULL, opts->perms, *nvlp);
|
|
|
|
} else {
|
|
|
|
char *curr = opts->who;
|
|
|
|
char *end = curr + strlen(curr);
|
|
|
|
|
|
|
|
while (curr < end) {
|
|
|
|
const char *who;
|
2011-08-20 03:24:04 +04:00
|
|
|
zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;
|
2011-07-27 02:44:36 +04:00
|
|
|
char *endch;
|
|
|
|
char *delim = strchr(curr, ',');
|
|
|
|
char errbuf[256];
|
|
|
|
char id[64];
|
|
|
|
struct passwd *p = NULL;
|
|
|
|
struct group *g = NULL;
|
|
|
|
|
|
|
|
uid_t rid;
|
|
|
|
if (delim == NULL)
|
|
|
|
delim = end;
|
|
|
|
else
|
|
|
|
*delim = '\0';
|
|
|
|
|
|
|
|
rid = (uid_t)strtol(curr, &endch, 0);
|
|
|
|
if (opts->user) {
|
|
|
|
who_type = ZFS_DELEG_USER;
|
|
|
|
if (*endch != '\0')
|
|
|
|
p = getpwnam(curr);
|
|
|
|
else
|
|
|
|
p = getpwuid(rid);
|
|
|
|
|
|
|
|
if (p != NULL)
|
|
|
|
rid = p->pw_uid;
|
|
|
|
else {
|
|
|
|
(void) snprintf(errbuf, 256, gettext(
|
|
|
|
"invalid user %s"), curr);
|
|
|
|
allow_usage(un, B_TRUE, errbuf);
|
|
|
|
}
|
|
|
|
} else if (opts->group) {
|
|
|
|
who_type = ZFS_DELEG_GROUP;
|
|
|
|
if (*endch != '\0')
|
|
|
|
g = getgrnam(curr);
|
|
|
|
else
|
|
|
|
g = getgrgid(rid);
|
|
|
|
|
|
|
|
if (g != NULL)
|
|
|
|
rid = g->gr_gid;
|
|
|
|
else {
|
|
|
|
(void) snprintf(errbuf, 256, gettext(
|
|
|
|
"invalid group %s"), curr);
|
|
|
|
allow_usage(un, B_TRUE, errbuf);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (*endch != '\0') {
|
|
|
|
p = getpwnam(curr);
|
|
|
|
} else {
|
|
|
|
p = getpwuid(rid);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p == NULL) {
|
|
|
|
if (*endch != '\0') {
|
|
|
|
g = getgrnam(curr);
|
|
|
|
} else {
|
|
|
|
g = getgrgid(rid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p != NULL) {
|
|
|
|
who_type = ZFS_DELEG_USER;
|
|
|
|
rid = p->pw_uid;
|
|
|
|
} else if (g != NULL) {
|
|
|
|
who_type = ZFS_DELEG_GROUP;
|
|
|
|
rid = g->gr_gid;
|
|
|
|
} else {
|
|
|
|
(void) snprintf(errbuf, 256, gettext(
|
|
|
|
"invalid user/group %s"), curr);
|
|
|
|
allow_usage(un, B_TRUE, errbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) sprintf(id, "%u", rid);
|
|
|
|
who = id;
|
|
|
|
|
|
|
|
store_allow_perm(who_type, opts->local,
|
|
|
|
opts->descend, who, opts->perms, *nvlp);
|
|
|
|
curr = delim + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_set_creat_perms(uu_avl_t *who_avl)
|
|
|
|
{
|
|
|
|
const char *sc_title[] = {
|
|
|
|
gettext("Permission sets:\n"),
|
|
|
|
gettext("Create time permissions:\n"),
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
who_perm_node_t *who_node = NULL;
|
|
|
|
int prev_weight = -1;
|
|
|
|
|
|
|
|
for (who_node = uu_avl_first(who_avl); who_node != NULL;
|
|
|
|
who_node = uu_avl_next(who_avl, who_node)) {
|
|
|
|
uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
|
|
|
|
zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
|
|
|
|
const char *who_name = who_node->who_perm.who_name;
|
|
|
|
int weight = who_type2weight(who_type);
|
|
|
|
boolean_t first = B_TRUE;
|
|
|
|
deleg_perm_node_t *deleg_node;
|
|
|
|
|
|
|
|
if (prev_weight != weight) {
|
2018-09-06 23:11:21 +03:00
|
|
|
(void) printf("%s", sc_title[weight]);
|
2011-07-27 02:44:36 +04:00
|
|
|
prev_weight = weight;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (who_name == NULL || strnlen(who_name, 1) == 0)
|
|
|
|
(void) printf("\t");
|
|
|
|
else
|
|
|
|
(void) printf("\t%s ", who_name);
|
|
|
|
|
|
|
|
for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
|
|
|
|
deleg_node = uu_avl_next(avl, deleg_node)) {
|
|
|
|
if (first) {
|
|
|
|
(void) printf("%s",
|
|
|
|
deleg_node->dpn_perm.dp_name);
|
|
|
|
first = B_FALSE;
|
|
|
|
} else
|
|
|
|
(void) printf(",%s",
|
|
|
|
deleg_node->dpn_perm.dp_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) printf("\n");
|
|
|
|
}
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2017-01-18 01:45:02 +03:00
|
|
|
static void
|
2011-07-27 02:44:36 +04:00
|
|
|
print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
|
|
|
|
const char *title)
|
|
|
|
{
|
|
|
|
who_perm_node_t *who_node = NULL;
|
|
|
|
boolean_t prt_title = B_TRUE;
|
|
|
|
uu_avl_walk_t *walk;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
while ((who_node = uu_avl_walk_next(walk)) != NULL) {
|
|
|
|
const char *who_name = who_node->who_perm.who_name;
|
|
|
|
const char *nice_who_name = who_node->who_perm.who_ug_name;
|
|
|
|
uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
|
|
|
|
zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
|
|
|
|
char delim = ' ';
|
|
|
|
deleg_perm_node_t *deleg_node;
|
|
|
|
boolean_t prt_who = B_TRUE;
|
|
|
|
|
|
|
|
for (deleg_node = uu_avl_first(avl);
|
|
|
|
deleg_node != NULL;
|
|
|
|
deleg_node = uu_avl_next(avl, deleg_node)) {
|
|
|
|
if (local != deleg_node->dpn_perm.dp_local ||
|
|
|
|
descend != deleg_node->dpn_perm.dp_descend)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (prt_who) {
|
|
|
|
const char *who = NULL;
|
|
|
|
if (prt_title) {
|
|
|
|
prt_title = B_FALSE;
|
2011-08-20 02:12:44 +04:00
|
|
|
(void) printf("%s", title);
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (who_type) {
|
|
|
|
case ZFS_DELEG_USER_SETS:
|
|
|
|
case ZFS_DELEG_USER:
|
|
|
|
who = gettext("user");
|
|
|
|
if (nice_who_name)
|
|
|
|
who_name = nice_who_name;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_GROUP_SETS:
|
|
|
|
case ZFS_DELEG_GROUP:
|
|
|
|
who = gettext("group");
|
|
|
|
if (nice_who_name)
|
|
|
|
who_name = nice_who_name;
|
|
|
|
break;
|
|
|
|
case ZFS_DELEG_EVERYONE_SETS:
|
|
|
|
case ZFS_DELEG_EVERYONE:
|
|
|
|
who = gettext("everyone");
|
|
|
|
who_name = NULL;
|
|
|
|
break;
|
2017-01-18 01:45:02 +03:00
|
|
|
|
|
|
|
default:
|
|
|
|
assert(who != NULL);
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
prt_who = B_FALSE;
|
|
|
|
if (who_name == NULL)
|
|
|
|
(void) printf("\t%s", who);
|
|
|
|
else
|
|
|
|
(void) printf("\t%s %s", who, who_name);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
(void) printf("%c%s", delim,
|
|
|
|
deleg_node->dpn_perm.dp_name);
|
|
|
|
delim = ',';
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
if (!prt_who)
|
|
|
|
(void) printf("\n");
|
|
|
|
}
|
2010-05-29 00:45:14 +04:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
uu_avl_walk_end(walk);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
static void
|
|
|
|
print_fs_perms(fs_perm_set_t *fspset)
|
|
|
|
{
|
|
|
|
fs_perm_node_t *node = NULL;
|
2016-06-16 00:28:36 +03:00
|
|
|
char buf[MAXNAMELEN + 32];
|
2011-07-27 02:44:36 +04:00
|
|
|
const char *dsname = buf;
|
|
|
|
|
|
|
|
for (node = uu_list_first(fspset->fsps_list); node != NULL;
|
|
|
|
node = uu_list_next(fspset->fsps_list, node)) {
|
|
|
|
uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
|
|
|
|
uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
|
|
|
|
int left = 0;
|
|
|
|
|
2016-06-16 00:28:36 +03:00
|
|
|
(void) snprintf(buf, sizeof (buf),
|
2011-07-27 02:44:36 +04:00
|
|
|
gettext("---- Permissions on %s "),
|
|
|
|
node->fspn_fsperm.fsp_name);
|
2011-08-20 02:12:44 +04:00
|
|
|
(void) printf("%s", dsname);
|
2011-07-27 02:44:36 +04:00
|
|
|
left = 70 - strlen(buf);
|
|
|
|
while (left-- > 0)
|
|
|
|
(void) printf("-");
|
|
|
|
(void) printf("\n");
|
|
|
|
|
|
|
|
print_set_creat_perms(sc_avl);
|
|
|
|
print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
|
|
|
|
gettext("Local permissions:\n"));
|
|
|
|
print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
|
|
|
|
gettext("Descendent permissions:\n"));
|
|
|
|
print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
|
|
|
|
gettext("Local+Descendent permissions:\n"));
|
2010-05-29 00:45:14 +04:00
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
|
|
|
|
|
|
|
|
struct deleg_perms {
|
|
|
|
boolean_t un;
|
|
|
|
nvlist_t *nvl;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_deleg_perms(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
struct deleg_perms *perms = (struct deleg_perms *)data;
|
|
|
|
zfs_type_t zfs_type = zfs_get_type(zhp);
|
|
|
|
|
|
|
|
if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-07-27 02:44:36 +04:00
|
|
|
zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
|
2008-11-20 23:01:55 +03:00
|
|
|
{
|
2011-07-27 02:44:36 +04:00
|
|
|
zfs_handle_t *zhp;
|
|
|
|
nvlist_t *perm_nvl = NULL;
|
|
|
|
nvlist_t *update_perm_nvl = NULL;
|
|
|
|
int error = 1;
|
|
|
|
int c;
|
|
|
|
struct allow_opts opts = { 0 };
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
const char *optstr = un ? "ldugecsrh" : "ldugecsh";
|
|
|
|
|
|
|
|
/* check opts */
|
|
|
|
while ((c = getopt(argc, argv, optstr)) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'l':
|
|
|
|
opts.local = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'd':
|
2011-07-27 02:44:36 +04:00
|
|
|
opts.descend = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'u':
|
|
|
|
opts.user = B_TRUE;
|
2010-05-29 00:45:14 +04:00
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'g':
|
|
|
|
opts.group = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'e':
|
|
|
|
opts.everyone = B_TRUE;
|
2009-02-18 23:51:31 +03:00
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 's':
|
|
|
|
opts.set = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'c':
|
|
|
|
opts.create = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
opts.recursive = B_TRUE;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing argument for "
|
|
|
|
"'%c' option\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
2011-07-27 02:44:36 +04:00
|
|
|
case 'h':
|
|
|
|
opts.prt_usage = B_TRUE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
/* check arguments */
|
|
|
|
parse_allow_args(argc, argv, un, &opts);
|
|
|
|
|
|
|
|
/* try to open the dataset */
|
2011-11-28 14:13:54 +04:00
|
|
|
if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
|
|
|
|
ZFS_TYPE_VOLUME)) == NULL) {
|
|
|
|
(void) fprintf(stderr, "Failed to open dataset: %s\n",
|
2011-07-27 02:44:36 +04:00
|
|
|
opts.dataset);
|
|
|
|
return (-1);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
|
|
|
|
goto cleanup2;
|
|
|
|
|
|
|
|
fs_perm_set_init(&fs_perm_set);
|
|
|
|
if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
|
2011-11-28 14:13:54 +04:00
|
|
|
(void) fprintf(stderr, "Failed to parse fsacl permissions\n");
|
2011-07-27 02:44:36 +04:00
|
|
|
goto cleanup1;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
if (opts.prt_perms)
|
|
|
|
print_fs_perms(&fs_perm_set);
|
|
|
|
else {
|
|
|
|
(void) construct_fsacl_list(un, &opts, &update_perm_nvl);
|
|
|
|
if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
|
|
|
|
goto cleanup0;
|
|
|
|
|
|
|
|
if (un && opts.recursive) {
|
|
|
|
struct deleg_perms data = { un, update_perm_nvl };
|
|
|
|
if (zfs_iter_filesystems(zhp, set_deleg_perms,
|
|
|
|
&data) != 0)
|
|
|
|
goto cleanup0;
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
error = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
cleanup0:
|
|
|
|
nvlist_free(perm_nvl);
|
2016-04-01 06:54:07 +03:00
|
|
|
nvlist_free(update_perm_nvl);
|
2011-07-27 02:44:36 +04:00
|
|
|
cleanup1:
|
|
|
|
fs_perm_set_fini(&fs_perm_set);
|
|
|
|
cleanup2:
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_allow(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_unallow(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2009-08-18 22:43:27 +04:00
|
|
|
static int
|
|
|
|
zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
|
|
|
|
{
|
|
|
|
int errors = 0;
|
|
|
|
int i;
|
|
|
|
const char *tag;
|
|
|
|
boolean_t recursive = B_FALSE;
|
2010-05-29 00:45:14 +04:00
|
|
|
const char *opts = holding ? "rt" : "r";
|
2009-08-18 22:43:27 +04:00
|
|
|
int c;
|
|
|
|
|
|
|
|
/* check options */
|
2010-05-29 00:45:14 +04:00
|
|
|
while ((c = getopt(argc, argv, opts)) != -1) {
|
2009-08-18 22:43:27 +04:00
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
|
|
|
recursive = B_TRUE;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 2)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
|
|
|
tag = argv[0];
|
|
|
|
--argc;
|
|
|
|
++argv;
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (holding && tag[0] == '.') {
|
|
|
|
/* tags starting with '.' are reserved for libzfs */
|
|
|
|
(void) fprintf(stderr, gettext("tag may not start with '.'\n"));
|
|
|
|
usage(B_FALSE);
|
2009-08-18 22:43:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
zfs_handle_t *zhp;
|
2016-06-16 00:28:36 +03:00
|
|
|
char parent[ZFS_MAX_DATASET_NAME_LEN];
|
2009-08-18 22:43:27 +04:00
|
|
|
const char *delim;
|
|
|
|
char *path = argv[i];
|
|
|
|
|
|
|
|
delim = strchr(path, '@');
|
|
|
|
if (delim == NULL) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'%s' is not a snapshot\n"), path);
|
|
|
|
++errors;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
(void) strncpy(parent, path, delim - path);
|
|
|
|
parent[delim - path] = '\0';
|
|
|
|
|
|
|
|
zhp = zfs_open(g_zfs, parent,
|
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL) {
|
|
|
|
++errors;
|
|
|
|
continue;
|
|
|
|
}
|
2010-05-29 00:45:14 +04:00
|
|
|
if (holding) {
|
2013-05-25 06:06:23 +04:00
|
|
|
if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)
|
2010-05-29 00:45:14 +04:00
|
|
|
++errors;
|
|
|
|
} else {
|
|
|
|
if (zfs_release(zhp, delim+1, tag, recursive) != 0)
|
|
|
|
++errors;
|
|
|
|
}
|
2009-08-18 22:43:27 +04:00
|
|
|
zfs_close(zhp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (errors != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-29 00:45:14 +04:00
|
|
|
* zfs hold [-r] [-t] <tag> <snap> ...
|
2009-08-18 22:43:27 +04:00
|
|
|
*
|
2010-05-29 00:45:14 +04:00
|
|
|
* -r Recursively hold
|
2009-08-18 22:43:27 +04:00
|
|
|
*
|
|
|
|
* Apply a user-hold with the given tag to the list of snapshots.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_hold(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* zfs release [-r] <tag> <snap> ...
|
|
|
|
*
|
2010-05-29 00:45:14 +04:00
|
|
|
* -r Recursively release
|
2009-08-18 22:43:27 +04:00
|
|
|
*
|
|
|
|
* Release a user-hold with the given tag from the list of snapshots.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_release(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
|
|
|
|
}
|
|
|
|
|
2011-07-27 02:44:36 +04:00
|
|
|
typedef struct holds_cbdata {
|
|
|
|
boolean_t cb_recursive;
|
|
|
|
const char *cb_snapname;
|
|
|
|
nvlist_t **cb_nvlp;
|
|
|
|
size_t cb_max_namelen;
|
|
|
|
size_t cb_max_taglen;
|
|
|
|
} holds_cbdata_t;
|
|
|
|
|
2020-03-26 18:28:22 +03:00
|
|
|
#define STRFTIME_FMT_STR "%a %b %e %H:%M %Y"
|
2011-07-27 02:44:36 +04:00
|
|
|
#define DATETIME_BUF_LEN (32)
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
print_holds(boolean_t scripted, int nwidth, int tagwidth, nvlist_t *nvl)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
nvpair_t *nvp = NULL;
|
|
|
|
char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
|
|
|
|
const char *col;
|
|
|
|
|
|
|
|
if (!scripted) {
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
col = gettext(hdr_cols[i]);
|
|
|
|
if (i < 2)
|
|
|
|
(void) printf("%-*s ", i ? tagwidth : nwidth,
|
|
|
|
col);
|
|
|
|
else
|
|
|
|
(void) printf("%s\n", col);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
|
|
|
|
char *zname = nvpair_name(nvp);
|
|
|
|
nvlist_t *nvl2;
|
|
|
|
nvpair_t *nvp2 = NULL;
|
|
|
|
(void) nvpair_value_nvlist(nvp, &nvl2);
|
|
|
|
while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
|
|
|
|
char tsbuf[DATETIME_BUF_LEN];
|
|
|
|
char *tagname = nvpair_name(nvp2);
|
|
|
|
uint64_t val = 0;
|
|
|
|
time_t time;
|
|
|
|
struct tm t;
|
|
|
|
|
|
|
|
(void) nvpair_value_uint64(nvp2, &val);
|
|
|
|
time = (time_t)val;
|
|
|
|
(void) localtime_r(&time, &t);
|
|
|
|
(void) strftime(tsbuf, DATETIME_BUF_LEN,
|
|
|
|
gettext(STRFTIME_FMT_STR), &t);
|
|
|
|
|
2017-11-01 04:41:46 +03:00
|
|
|
if (scripted) {
|
|
|
|
(void) printf("%s\t%s\t%s\n", zname,
|
|
|
|
tagname, tsbuf);
|
|
|
|
} else {
|
|
|
|
(void) printf("%-*s %-*s %s\n", nwidth,
|
|
|
|
zname, tagwidth, tagname, tsbuf);
|
|
|
|
}
|
2011-07-27 02:44:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic callback function to list a dataset or snapshot.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
holds_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
holds_cbdata_t *cbp = data;
|
|
|
|
nvlist_t *top_nvl = *cbp->cb_nvlp;
|
|
|
|
nvlist_t *nvl = NULL;
|
|
|
|
nvpair_t *nvp = NULL;
|
|
|
|
const char *zname = zfs_get_name(zhp);
|
2016-06-16 00:28:36 +03:00
|
|
|
size_t znamelen = strlen(zname);
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
if (cbp->cb_recursive) {
|
|
|
|
const char *snapname;
|
|
|
|
char *delim = strchr(zname, '@');
|
|
|
|
if (delim == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
snapname = delim + 1;
|
|
|
|
if (strcmp(cbp->cb_snapname, snapname))
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zfs_get_holds(zhp, &nvl) != 0)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
if (znamelen > cbp->cb_max_namelen)
|
|
|
|
cbp->cb_max_namelen = znamelen;
|
|
|
|
|
|
|
|
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
|
|
|
|
const char *tag = nvpair_name(nvp);
|
2016-06-16 00:28:36 +03:00
|
|
|
size_t taglen = strlen(tag);
|
2011-07-27 02:44:36 +04:00
|
|
|
if (taglen > cbp->cb_max_taglen)
|
|
|
|
cbp->cb_max_taglen = taglen;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (nvlist_add_nvlist(top_nvl, zname, nvl));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-08-19 01:47:41 +03:00
|
|
|
* zfs holds [-rH] <snap> ...
|
2011-07-27 02:44:36 +04:00
|
|
|
*
|
2018-08-19 01:47:41 +03:00
|
|
|
* -r Lists holds that are set on the named snapshots recursively.
|
|
|
|
* -H Scripted mode; elide headers and separate columns by tabs.
|
2011-07-27 02:44:36 +04:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_holds(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int errors = 0;
|
|
|
|
int c;
|
|
|
|
int i;
|
|
|
|
boolean_t scripted = B_FALSE;
|
|
|
|
boolean_t recursive = B_FALSE;
|
|
|
|
const char *opts = "rH";
|
|
|
|
nvlist_t *nvl;
|
|
|
|
|
|
|
|
int types = ZFS_TYPE_SNAPSHOT;
|
|
|
|
holds_cbdata_t cb = { 0 };
|
|
|
|
|
|
|
|
int limit = 0;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2011-07-27 02:44:36 +04:00
|
|
|
int flags = 0;
|
|
|
|
|
|
|
|
/* check options */
|
|
|
|
while ((c = getopt(argc, argv, opts)) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'r':
|
|
|
|
recursive = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
scripted = B_TRUE;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recursive) {
|
|
|
|
types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
|
|
|
|
flags |= ZFS_ITER_RECURSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
|
|
|
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
|
|
|
|
nomem();
|
|
|
|
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
char *snapshot = argv[i];
|
|
|
|
const char *delim;
|
|
|
|
const char *snapname;
|
|
|
|
|
|
|
|
delim = strchr(snapshot, '@');
|
|
|
|
if (delim == NULL) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'%s' is not a snapshot\n"), snapshot);
|
|
|
|
++errors;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
snapname = delim + 1;
|
|
|
|
if (recursive)
|
|
|
|
snapshot[delim - snapshot] = '\0';
|
|
|
|
|
|
|
|
cb.cb_recursive = recursive;
|
|
|
|
cb.cb_snapname = snapname;
|
|
|
|
cb.cb_nvlp = &nvl;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 1. collect holds data, set format options
|
|
|
|
*/
|
|
|
|
ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit,
|
|
|
|
holds_callback, &cb);
|
|
|
|
if (ret != 0)
|
|
|
|
++errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 2. print holds data
|
|
|
|
*/
|
|
|
|
print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
|
|
|
|
|
|
|
|
if (nvlist_empty(nvl))
|
2012-03-14 17:34:00 +04:00
|
|
|
(void) fprintf(stderr, gettext("no datasets available\n"));
|
2011-07-27 02:44:36 +04:00
|
|
|
|
|
|
|
nvlist_free(nvl);
|
|
|
|
|
|
|
|
return (0 != errors);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
#define CHECK_SPINNER 30
|
|
|
|
#define SPINNER_TIME 3 /* seconds */
|
2018-11-05 18:40:05 +03:00
|
|
|
#define MOUNT_TIME 1 /* seconds */
|
|
|
|
|
|
|
|
typedef struct get_all_state {
|
|
|
|
boolean_t ga_verbose;
|
|
|
|
get_all_cb_t *ga_cbp;
|
|
|
|
} get_all_state_t;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
static int
|
|
|
|
get_one_dataset(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
2010-05-29 00:45:14 +04:00
|
|
|
static char *spin[] = { "-", "\\", "|", "/" };
|
2008-11-20 23:01:55 +03:00
|
|
|
static int spinval = 0;
|
|
|
|
static int spincheck = 0;
|
|
|
|
static time_t last_spin_time = (time_t)0;
|
2018-11-05 18:40:05 +03:00
|
|
|
get_all_state_t *state = data;
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_type_t type = zfs_get_type(zhp);
|
|
|
|
|
2018-11-05 18:40:05 +03:00
|
|
|
if (state->ga_verbose) {
|
2008-11-20 23:01:55 +03:00
|
|
|
if (--spincheck < 0) {
|
|
|
|
time_t now = time(NULL);
|
|
|
|
if (last_spin_time + SPINNER_TIME < now) {
|
2010-05-29 00:45:14 +04:00
|
|
|
update_progress(spin[spinval++ % 4]);
|
2008-11-20 23:01:55 +03:00
|
|
|
last_spin_time = now;
|
|
|
|
}
|
|
|
|
spincheck = CHECK_SPINNER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-04-18 05:49:25 +04:00
|
|
|
* Iterate over any nested datasets.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
2010-08-27 01:24:34 +04:00
|
|
|
if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip any datasets whose type does not match.
|
|
|
|
*/
|
2010-08-27 01:24:34 +04:00
|
|
|
if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
2018-11-05 18:40:05 +03:00
|
|
|
libzfs_add_handle(state->ga_cbp, zhp);
|
|
|
|
assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-11-05 18:40:05 +03:00
|
|
|
get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
|
2008-11-20 23:01:55 +03:00
|
|
|
{
|
2018-11-05 18:40:05 +03:00
|
|
|
get_all_state_t state = {
|
|
|
|
.ga_verbose = verbose,
|
|
|
|
.ga_cbp = cbp
|
|
|
|
};
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (verbose)
|
|
|
|
set_progress_header(gettext("Reading ZFS config"));
|
2018-11-05 18:40:05 +03:00
|
|
|
(void) zfs_iter_root(g_zfs, get_one_dataset, &state);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (verbose)
|
|
|
|
finish_progress(gettext("done."));
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic callback for sharing or mounting filesystems. Because the code is so
|
|
|
|
* similar, we have a common function with an extra parameter to determine which
|
|
|
|
* mode we are using.
|
|
|
|
*/
|
2018-11-05 18:40:05 +03:00
|
|
|
typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;
|
|
|
|
|
|
|
|
typedef struct share_mount_state {
|
|
|
|
share_mount_op_t sm_op;
|
|
|
|
boolean_t sm_verbose;
|
|
|
|
int sm_flags;
|
|
|
|
char *sm_options;
|
|
|
|
char *sm_proto; /* only valid for OP_SHARE */
|
|
|
|
pthread_mutex_t sm_lock; /* protects the remaining fields */
|
|
|
|
uint_t sm_total; /* number of filesystems to process */
|
|
|
|
uint_t sm_done; /* number of filesystems processed */
|
|
|
|
int sm_status; /* -1 if any of the share/mount operations failed */
|
|
|
|
} share_mount_state_t;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Share or mount a dataset.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
|
|
|
|
boolean_t explicit, const char *options)
|
|
|
|
{
|
|
|
|
char mountpoint[ZFS_MAXPROPLEN];
|
|
|
|
char shareopts[ZFS_MAXPROPLEN];
|
|
|
|
char smbshareopts[ZFS_MAXPROPLEN];
|
|
|
|
const char *cmdname = op == OP_SHARE ? "share" : "mount";
|
|
|
|
struct mnttab mnt;
|
|
|
|
uint64_t zoned, canmount;
|
|
|
|
boolean_t shared_nfs, shared_smb;
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
/*
|
|
|
|
* Check to make sure we can mount/share this dataset. If we
|
|
|
|
* are in the global zone and the filesystem is exported to a
|
|
|
|
* local zone, or if we are in a local zone and the
|
|
|
|
* filesystem is not exported, then it is an error.
|
|
|
|
*/
|
|
|
|
zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if (zoned && getzoneid() == GLOBAL_ZONEID) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': "
|
|
|
|
"dataset is exported to a local zone\n"), cmdname,
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
return (1);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
} else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': "
|
|
|
|
"permission denied\n"), cmdname,
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
return (1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
/*
|
|
|
|
* Ignore any filesystems which don't apply to us. This
|
|
|
|
* includes those with a legacy mountpoint, or those with
|
|
|
|
* legacy share options.
|
|
|
|
*/
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
|
|
|
|
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
|
|
|
|
sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
|
|
|
|
sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
|
|
|
|
if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
|
|
|
|
strcmp(smbshareopts, "off") == 0) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
(void) fprintf(stderr, gettext("cannot share '%s': "
|
|
|
|
"legacy share\n"), zfs_get_name(zhp));
|
|
|
|
(void) fprintf(stderr, gettext("use share(1M) to "
|
|
|
|
"share this filesystem, or set "
|
|
|
|
"sharenfs property on\n"));
|
|
|
|
return (1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
/*
|
|
|
|
* We cannot share or mount legacy filesystems. If the
|
|
|
|
* shareopts is non-legacy but the mountpoint is legacy, we
|
|
|
|
* treat it as a legacy share.
|
|
|
|
*/
|
|
|
|
if (strcmp(mountpoint, "legacy") == 0) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': "
|
|
|
|
"legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
|
|
|
|
(void) fprintf(stderr, gettext("use %s(1M) to "
|
|
|
|
"%s this filesystem\n"), cmdname, cmdname);
|
|
|
|
return (1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if (strcmp(mountpoint, "none") == 0) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': no "
|
|
|
|
"mountpoint set\n"), cmdname, zfs_get_name(zhp));
|
|
|
|
return (1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
/*
|
|
|
|
* canmount explicit outcome
|
|
|
|
* on no pass through
|
|
|
|
* on yes pass through
|
|
|
|
* off no return 0
|
|
|
|
* off yes display error, return 1
|
|
|
|
* noauto no return 0
|
|
|
|
* noauto yes pass through
|
|
|
|
*/
|
|
|
|
canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
|
|
|
|
if (canmount == ZFS_CANMOUNT_OFF) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': "
|
|
|
|
"'canmount' property is set to 'off'\n"), cmdname,
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
return (1);
|
|
|
|
} else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2018-04-06 23:28:15 +03:00
|
|
|
/*
|
|
|
|
* If this filesystem is encrypted and does not have
|
|
|
|
* a loaded key, we can not mount it.
|
|
|
|
*/
|
|
|
|
if ((flags & MS_CRYPT) == 0 &&
|
|
|
|
zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&
|
|
|
|
zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==
|
|
|
|
ZFS_KEYSTATUS_UNAVAILABLE) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': "
|
|
|
|
"encryption key not loaded\n"), cmdname, zfs_get_name(zhp));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2016-01-07 00:22:48 +03:00
|
|
|
/*
|
|
|
|
* If this filesystem is inconsistent and has a receive resume
|
|
|
|
* token, we can not mount it.
|
|
|
|
*/
|
|
|
|
if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
|
|
|
|
zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
|
|
|
|
NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': "
|
|
|
|
"Contains partially-completed state from "
|
2018-02-12 23:28:59 +03:00
|
|
|
"\"zfs receive -s\", which can be resumed with "
|
2016-01-07 00:22:48 +03:00
|
|
|
"\"zfs send -t\"\n"),
|
|
|
|
cmdname, zfs_get_name(zhp));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE)) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': "
|
|
|
|
"Dataset is not complete, was created by receiving "
|
|
|
|
"a redacted zfs send stream.\n"), cmdname,
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
/*
|
|
|
|
* At this point, we have verified that the mountpoint and/or
|
|
|
|
* shareopts are appropriate for auto management. If the
|
|
|
|
* filesystem is already mounted or shared, return (failing
|
|
|
|
* for explicit requests); otherwise mount or share the
|
|
|
|
* filesystem.
|
|
|
|
*/
|
|
|
|
switch (op) {
|
|
|
|
case OP_SHARE:
|
|
|
|
|
|
|
|
shared_nfs = zfs_is_shared_nfs(zhp, NULL);
|
|
|
|
shared_smb = zfs_is_shared_smb(zhp, NULL);
|
|
|
|
|
2010-12-17 01:05:42 +03:00
|
|
|
if ((shared_nfs && shared_smb) ||
|
2017-01-18 01:45:02 +03:00
|
|
|
(shared_nfs && strcmp(shareopts, "on") == 0 &&
|
|
|
|
strcmp(smbshareopts, "off") == 0) ||
|
|
|
|
(shared_smb && strcmp(smbshareopts, "on") == 0 &&
|
|
|
|
strcmp(shareopts, "off") == 0)) {
|
2008-11-20 23:01:55 +03:00
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
(void) fprintf(stderr, gettext("cannot share "
|
|
|
|
"'%s': filesystem already shared\n"),
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_get_name(zhp));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if (!zfs_is_mounted(zhp, NULL) &&
|
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 20:36:48 +03:00
|
|
|
zfs_mount(zhp, NULL, flags) != 0)
|
2010-08-27 01:24:34 +04:00
|
|
|
return (1);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if (protocol == NULL) {
|
|
|
|
if (zfs_shareall(zhp) != 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
2010-08-27 01:24:34 +04:00
|
|
|
} else if (strcmp(protocol, "nfs") == 0) {
|
|
|
|
if (zfs_share_nfs(zhp))
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
2010-08-27 01:24:34 +04:00
|
|
|
} else if (strcmp(protocol, "smb") == 0) {
|
|
|
|
if (zfs_share_smb(zhp))
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
2010-08-27 01:24:34 +04:00
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr, gettext("cannot share "
|
|
|
|
"'%s': invalid share type '%s' "
|
|
|
|
"specified\n"),
|
|
|
|
zfs_get_name(zhp), protocol);
|
|
|
|
return (1);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
case OP_MOUNT:
|
|
|
|
if (options == NULL)
|
|
|
|
mnt.mnt_mntopts = "";
|
|
|
|
else
|
|
|
|
mnt.mnt_mntopts = (char *)options;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
|
|
|
|
zfs_is_mounted(zhp, NULL)) {
|
|
|
|
if (!explicit)
|
|
|
|
return (0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
(void) fprintf(stderr, gettext("cannot mount "
|
|
|
|
"'%s': filesystem already mounted\n"),
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
return (1);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2010-08-27 01:24:34 +04:00
|
|
|
|
2019-06-23 02:41:21 +03:00
|
|
|
if (zfs_mount(zhp, options, flags) != 0) {
|
|
|
|
/*
|
|
|
|
* Check if a mount sneaked in after we checked
|
|
|
|
*/
|
|
|
|
if (!explicit &&
|
|
|
|
libzfs_errno(g_zfs) == EZFS_MOUNTFAILED) {
|
|
|
|
usleep(10 * MILLISEC);
|
|
|
|
libzfs_mnttab_cache(g_zfs, B_FALSE);
|
|
|
|
|
|
|
|
if (zfs_is_mounted(zhp, NULL)) {
|
|
|
|
(void) fprintf(stderr, gettext(
|
|
|
|
"Ignoring previous 'already "
|
|
|
|
"mounted' error for '%s'\n"),
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
2010-08-27 01:24:34 +04:00
|
|
|
return (1);
|
2019-06-23 02:41:21 +03:00
|
|
|
}
|
2010-08-27 01:24:34 +04:00
|
|
|
break;
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reports progress in the form "(current/total)". Not thread-safe.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
report_mount_progress(int current, int total)
|
|
|
|
{
|
2010-05-29 00:45:14 +04:00
|
|
|
static time_t last_progress_time = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
time_t now = time(NULL);
|
2010-05-29 00:45:14 +04:00
|
|
|
char info[32];
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* report 1..n instead of 0..n-1 */
|
|
|
|
++current;
|
|
|
|
|
|
|
|
/* display header if we're here for the first time */
|
|
|
|
if (current == 1) {
|
2010-05-29 00:45:14 +04:00
|
|
|
set_progress_header(gettext("Mounting ZFS filesystems"));
|
2008-11-20 23:01:55 +03:00
|
|
|
} else if (current != total && last_progress_time + MOUNT_TIME >= now) {
|
|
|
|
/* too soon to report again */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
last_progress_time = now;
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
(void) sprintf(info, "(%d/%d)", current, total);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (current == total)
|
|
|
|
finish_progress(info);
|
|
|
|
else
|
|
|
|
update_progress(info);
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2018-11-05 18:40:05 +03:00
|
|
|
/*
|
|
|
|
* zfs_foreach_mountpoint() callback that mounts or shares one filesystem and
|
|
|
|
* updates the progress meter.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
share_mount_one_cb(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
share_mount_state_t *sms = arg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,
|
|
|
|
B_FALSE, sms->sm_options);
|
|
|
|
|
|
|
|
pthread_mutex_lock(&sms->sm_lock);
|
|
|
|
if (ret != 0)
|
|
|
|
sms->sm_status = ret;
|
|
|
|
sms->sm_done++;
|
|
|
|
if (sms->sm_verbose)
|
|
|
|
report_mount_progress(sms->sm_done, sms->sm_total);
|
|
|
|
pthread_mutex_unlock(&sms->sm_lock);
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
static void
|
|
|
|
append_options(char *mntopts, char *newopts)
|
|
|
|
{
|
|
|
|
int len = strlen(mntopts);
|
|
|
|
|
|
|
|
/* original length plus new string to append plus 1 for the comma */
|
|
|
|
if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
|
|
|
|
(void) fprintf(stderr, gettext("the opts argument for "
|
2010-08-26 20:52:39 +04:00
|
|
|
"'%s' option is too long (more than %d chars)\n"),
|
2008-11-20 23:01:55 +03:00
|
|
|
"-o", MNT_LINE_MAX);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*mntopts)
|
|
|
|
mntopts[len++] = ',';
|
|
|
|
|
|
|
|
(void) strcpy(&mntopts[len], newopts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
share_mount(int op, int argc, char **argv)
|
|
|
|
{
|
|
|
|
int do_all = 0;
|
|
|
|
boolean_t verbose = B_FALSE;
|
|
|
|
int c, ret = 0;
|
|
|
|
char *options = NULL;
|
2010-08-27 01:24:34 +04:00
|
|
|
int flags = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
/* check options */
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:Of" : "al"))
|
2008-11-20 23:01:55 +03:00
|
|
|
!= -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'a':
|
|
|
|
do_all = 1;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
verbose = B_TRUE;
|
|
|
|
break;
|
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 20:36:48 +03:00
|
|
|
case 'l':
|
|
|
|
flags |= MS_CRYPT;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case 'o':
|
|
|
|
if (*optarg == '\0') {
|
|
|
|
(void) fprintf(stderr, gettext("empty mount "
|
|
|
|
"options (-o) specified\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options == NULL)
|
|
|
|
options = safe_malloc(MNT_LINE_MAX + 1);
|
|
|
|
|
|
|
|
/* option validation is done later */
|
|
|
|
append_options(options, optarg);
|
|
|
|
break;
|
2012-01-12 04:48:02 +04:00
|
|
|
case 'O':
|
|
|
|
flags |= MS_OVERLAY;
|
|
|
|
break;
|
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to
a target system. One possible use case for this feature is to not
transmit sensitive information to a data warehousing, test/dev, or
analytics environment. Another is to save space by not replicating
unimportant data within a given dataset, for example in backup tools
like zrepl.
Redacted send/receive is a three-stage process. First, a clone (or
clones) is made of the snapshot to be sent to the target. In this
clone (or clones), all unnecessary or unwanted data is removed or
modified. This clone is then snapshotted to create the "redaction
snapshot" (or snapshots). Second, the new zfs redact command is used
to create a redaction bookmark. The redaction bookmark stores the
list of blocks in a snapshot that were modified by the redaction
snapshot(s). Finally, the redaction bookmark is passed as a parameter
to zfs send. When sending to the snapshot that was redacted, the
redaction bookmark is used to filter out blocks that contain sensitive
or unwanted information, and those blocks are not included in the send
stream. When sending from the redaction bookmark, the blocks it
contains are considered as candidate blocks in addition to those
blocks in the destination snapshot that were modified since the
creation_txg of the redaction bookmark. This step is necessary to
allow the target to rehydrate data in the case where some blocks are
accidentally or unnecessarily modified in the redaction snapshot.
The changes to bookmarks to enable fast space estimation involve
adding deadlists to bookmarks. There is also logic to manage the
life cycles of these deadlists.
The new size estimation process operates in cases where previously
an accurate estimate could not be provided. In those cases, a send
is performed where no data blocks are read, reducing the runtime
significantly and providing a byte-accurate size estimate.
Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com>
Reviewed-by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Prashanth Sreenivasa <pks@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: Chris Williamson <chris.williamson@delphix.com>
Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com>
Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Dagnelie <pcd@delphix.com>
Closes #7958
2019-06-19 19:48:13 +03:00
|
|
|
case 'f':
|
|
|
|
flags |= MS_FORCE;
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing argument for "
|
|
|
|
"'%c' option\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (do_all) {
|
|
|
|
char *protocol = NULL;
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if (op == OP_SHARE && argc > 0) {
|
|
|
|
if (strcmp(argv[0], "nfs") != 0 &&
|
|
|
|
strcmp(argv[0], "smb") != 0) {
|
2008-11-20 23:01:55 +03:00
|
|
|
(void) fprintf(stderr, gettext("share type "
|
2010-05-29 00:45:14 +04:00
|
|
|
"must be 'nfs' or 'smb'\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
protocol = argv[0];
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
start_progress_timer();
|
2018-11-05 18:40:05 +03:00
|
|
|
get_all_cb_t cb = { 0 };
|
|
|
|
get_all_datasets(&cb, verbose);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2018-11-05 18:40:05 +03:00
|
|
|
if (cb.cb_used == 0) {
|
2016-10-05 04:15:57 +03:00
|
|
|
if (options != NULL)
|
|
|
|
free(options);
|
2008-11-20 23:01:55 +03:00
|
|
|
return (0);
|
2016-10-05 04:15:57 +03:00
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2018-11-05 18:40:05 +03:00
|
|
|
share_mount_state_t share_mount_state = { 0 };
|
|
|
|
share_mount_state.sm_op = op;
|
|
|
|
share_mount_state.sm_verbose = verbose;
|
|
|
|
share_mount_state.sm_flags = flags;
|
|
|
|
share_mount_state.sm_options = options;
|
|
|
|
share_mount_state.sm_proto = protocol;
|
|
|
|
share_mount_state.sm_total = cb.cb_used;
|
|
|
|
pthread_mutex_init(&share_mount_state.sm_lock, NULL);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2018-11-05 18:40:05 +03:00
|
|
|
/*
|
|
|
|
* libshare isn't mt-safe, so only do the operation in parallel
|
2019-05-25 23:46:32 +03:00
|
|
|
* if we're mounting. Additionally, the key-loading option must
|
|
|
|
* be serialized so that we can prompt the user for their keys
|
|
|
|
* in a consistent manner.
|
2018-11-05 18:40:05 +03:00
|
|
|
*/
|
|
|
|
zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
|
2019-05-25 23:46:32 +03:00
|
|
|
share_mount_one_cb, &share_mount_state,
|
|
|
|
op == OP_MOUNT && !(flags & MS_CRYPT));
|
2018-11-05 18:40:05 +03:00
|
|
|
ret = share_mount_state.sm_status;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2018-11-05 18:40:05 +03:00
|
|
|
for (int i = 0; i < cb.cb_used; i++)
|
|
|
|
zfs_close(cb.cb_handles[i]);
|
|
|
|
free(cb.cb_handles);
|
2008-11-20 23:01:55 +03:00
|
|
|
} else if (argc == 0) {
|
|
|
|
struct mnttab entry;
|
|
|
|
|
|
|
|
if ((op == OP_SHARE) || (options != NULL)) {
|
|
|
|
(void) fprintf(stderr, gettext("missing filesystem "
|
|
|
|
"argument (specify -a for all)\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-09-20 20:07:58 +03:00
|
|
|
* When mount is given no arguments, go through
|
|
|
|
* /proc/self/mounts and display any active ZFS mounts.
|
|
|
|
* We hide any snapshots, since they are controlled
|
|
|
|
* automatically.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
2014-03-26 21:17:17 +04:00
|
|
|
|
|
|
|
/* Reopen MNTTAB to prevent reading stale data from open file */
|
2016-10-05 04:15:57 +03:00
|
|
|
if (freopen(MNTTAB, "r", mnttab_file) == NULL) {
|
|
|
|
if (options != NULL)
|
|
|
|
free(options);
|
2014-03-26 21:17:17 +04:00
|
|
|
return (ENOENT);
|
2016-10-05 04:15:57 +03:00
|
|
|
}
|
2014-03-26 21:17:17 +04:00
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
while (getmntent(mnttab_file, &entry) == 0) {
|
|
|
|
if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
|
|
|
|
strchr(entry.mnt_special, '@') != NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
(void) printf("%-30s %s\n", entry.mnt_special,
|
|
|
|
entry.mnt_mountp);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if ((zhp = zfs_open(g_zfs, argv[0],
|
|
|
|
ZFS_TYPE_FILESYSTEM)) == NULL) {
|
2008-11-20 23:01:55 +03:00
|
|
|
ret = 1;
|
|
|
|
} else {
|
|
|
|
ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
|
|
|
|
options);
|
|
|
|
zfs_close(zhp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-05 04:15:57 +03:00
|
|
|
if (options != NULL)
|
|
|
|
free(options);
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-29 00:45:14 +04:00
|
|
|
* zfs mount -a [nfs]
|
2008-11-20 23:01:55 +03:00
|
|
|
* zfs mount filesystem
|
|
|
|
*
|
|
|
|
* Mount all filesystems, or mount the given filesystem.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_mount(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (share_mount(OP_MOUNT, argc, argv));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-29 00:45:14 +04:00
|
|
|
* zfs share -a [nfs | smb]
|
2008-11-20 23:01:55 +03:00
|
|
|
* zfs share filesystem
|
|
|
|
*
|
|
|
|
* Share all filesystems, or share the given filesystem.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_share(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (share_mount(OP_SHARE, argc, argv));
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct unshare_unmount_node {
|
|
|
|
zfs_handle_t *un_zhp;
|
|
|
|
char *un_mountp;
|
|
|
|
uu_avl_node_t un_avlnode;
|
|
|
|
} unshare_unmount_node_t;
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
|
|
|
|
{
|
|
|
|
const unshare_unmount_node_t *l = larg;
|
|
|
|
const unshare_unmount_node_t *r = rarg;
|
|
|
|
|
|
|
|
return (strcmp(l->un_mountp, r->un_mountp));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convenience routine used by zfs_do_umount() and manual_unmount(). Given an
|
2019-05-29 01:58:32 +03:00
|
|
|
* absolute path, find the entry /proc/self/mounts, verify that it's a
|
|
|
|
* ZFS filesystem, and unmount it appropriately.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
|
|
|
|
{
|
|
|
|
zfs_handle_t *zhp;
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
struct stat64 statbuf;
|
|
|
|
struct extmnttab entry;
|
|
|
|
const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
|
|
|
|
ino_t path_inode;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search for the given (major,minor) pair in the mount table.
|
|
|
|
*/
|
2014-03-26 21:17:17 +04:00
|
|
|
|
|
|
|
/* Reopen MNTTAB to prevent reading stale data from open file */
|
|
|
|
if (freopen(MNTTAB, "r", mnttab_file) == NULL)
|
|
|
|
return (ENOENT);
|
|
|
|
|
2019-10-02 20:39:48 +03:00
|
|
|
if (getextmntent(path, &entry, &statbuf) != 0) {
|
2008-11-20 23:01:55 +03:00
|
|
|
if (op == OP_SHARE) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': not "
|
|
|
|
"currently mounted\n"), cmdname, path);
|
|
|
|
return (1);
|
|
|
|
}
|
2016-09-20 20:07:58 +03:00
|
|
|
(void) fprintf(stderr, gettext("warning: %s not in"
|
|
|
|
"/proc/self/mounts\n"), path);
|
2008-11-20 23:01:55 +03:00
|
|
|
if ((ret = umount2(path, flags)) != 0)
|
|
|
|
(void) fprintf(stderr, gettext("%s: %s\n"), path,
|
|
|
|
strerror(errno));
|
|
|
|
return (ret != 0);
|
|
|
|
}
|
2019-10-02 20:39:48 +03:00
|
|
|
path_inode = statbuf.st_ino;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
|
|
|
|
"filesystem\n"), cmdname, path);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((zhp = zfs_open(g_zfs, entry.mnt_special,
|
|
|
|
ZFS_TYPE_FILESYSTEM)) == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
ret = 1;
|
2008-12-03 23:09:06 +03:00
|
|
|
if (stat64(entry.mnt_mountp, &statbuf) != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
|
|
|
|
cmdname, path, strerror(errno));
|
|
|
|
goto out;
|
|
|
|
} else if (statbuf.st_ino != path_inode) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"%s '%s': not a mountpoint\n"), cmdname, path);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
if (op == OP_SHARE) {
|
|
|
|
char nfs_mnt_prop[ZFS_MAXPROPLEN];
|
|
|
|
char smbshare_prop[ZFS_MAXPROPLEN];
|
|
|
|
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
|
|
|
|
sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
|
|
|
|
sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
|
|
|
|
if (strcmp(nfs_mnt_prop, "off") == 0 &&
|
|
|
|
strcmp(smbshare_prop, "off") == 0) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot unshare "
|
|
|
|
"'%s': legacy share\n"), path);
|
2010-12-17 02:43:37 +03:00
|
|
|
(void) fprintf(stderr, gettext("use exportfs(8) "
|
|
|
|
"or smbcontrol(1) to unshare this filesystem\n"));
|
2008-11-20 23:01:55 +03:00
|
|
|
} else if (!zfs_is_shared(zhp)) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot unshare '%s': "
|
|
|
|
"not currently shared\n"), path);
|
|
|
|
} else {
|
|
|
|
ret = zfs_unshareall_bypath(zhp, path);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char mtpt_prop[ZFS_MAXPROPLEN];
|
|
|
|
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
|
|
|
|
sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
if (is_manual) {
|
2008-11-20 23:01:55 +03:00
|
|
|
ret = zfs_unmount(zhp, NULL, flags);
|
|
|
|
} else if (strcmp(mtpt_prop, "legacy") == 0) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot unmount "
|
|
|
|
"'%s': legacy mountpoint\n"),
|
|
|
|
zfs_get_name(zhp));
|
2010-12-17 02:43:37 +03:00
|
|
|
(void) fprintf(stderr, gettext("use umount(8) "
|
2008-11-20 23:01:55 +03:00
|
|
|
"to unmount this filesystem\n"));
|
|
|
|
} else {
|
|
|
|
ret = zfs_unmountall(zhp, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-03 23:09:06 +03:00
|
|
|
out:
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (ret != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic callback for unsharing or unmounting a filesystem.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
unshare_unmount(int op, int argc, char **argv)
|
|
|
|
{
|
|
|
|
int do_all = 0;
|
|
|
|
int flags = 0;
|
|
|
|
int ret = 0;
|
2010-08-27 01:24:34 +04:00
|
|
|
int c;
|
2008-11-20 23:01:55 +03:00
|
|
|
zfs_handle_t *zhp;
|
2010-05-29 00:45:14 +04:00
|
|
|
char nfs_mnt_prop[ZFS_MAXPROPLEN];
|
2008-11-20 23:01:55 +03:00
|
|
|
char sharesmb[ZFS_MAXPROPLEN];
|
|
|
|
|
|
|
|
/* check options */
|
2019-06-28 22:38:37 +03:00
|
|
|
while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) {
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (c) {
|
|
|
|
case 'a':
|
|
|
|
do_all = 1;
|
|
|
|
break;
|
|
|
|
case 'f':
|
2019-06-28 22:38:37 +03:00
|
|
|
flags |= MS_FORCE;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
flags |= MS_CRYPT;
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
2016-11-29 22:22:38 +03:00
|
|
|
case ':':
|
|
|
|
(void) fprintf(stderr, gettext("missing argument for "
|
|
|
|
"'%c' option\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (do_all) {
|
|
|
|
/*
|
|
|
|
* We could make use of zfs_for_each() to walk all datasets in
|
|
|
|
* the system, but this would be very inefficient, especially
|
2016-09-20 20:07:58 +03:00
|
|
|
* since we would have to linearly search /proc/self/mounts for
|
|
|
|
* each one. Instead, do one pass through /proc/self/mounts
|
|
|
|
* looking for zfs entries and call zfs_unmount() for each one.
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* Things get a little tricky if the administrator has created
|
|
|
|
* mountpoints beneath other ZFS filesystems. In this case, we
|
|
|
|
* have to unmount the deepest filesystems first. To accomplish
|
|
|
|
* this, we place all the mountpoints in an AVL tree sorted by
|
|
|
|
* the special type (dataset name), and walk the result in
|
|
|
|
* reverse to make sure to get any snapshots first.
|
|
|
|
*/
|
|
|
|
struct mnttab entry;
|
|
|
|
uu_avl_pool_t *pool;
|
2010-12-17 01:05:42 +03:00
|
|
|
uu_avl_t *tree = NULL;
|
2008-11-20 23:01:55 +03:00
|
|
|
unshare_unmount_node_t *node;
|
|
|
|
uu_avl_index_t idx;
|
|
|
|
uu_avl_walk_t *walk;
|
2016-11-29 22:22:38 +03:00
|
|
|
char *protocol = NULL;
|
|
|
|
|
|
|
|
if (op == OP_SHARE && argc > 0) {
|
|
|
|
if (strcmp(argv[0], "nfs") != 0 &&
|
|
|
|
strcmp(argv[0], "smb") != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("share type "
|
|
|
|
"must be 'nfs' or 'smb'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
protocol = argv[0];
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
if (argc != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
2010-05-29 00:45:14 +04:00
|
|
|
if (((pool = uu_avl_pool_create("unmount_pool",
|
2008-11-20 23:01:55 +03:00
|
|
|
sizeof (unshare_unmount_node_t),
|
|
|
|
offsetof(unshare_unmount_node_t, un_avlnode),
|
2010-05-29 00:45:14 +04:00
|
|
|
unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
|
|
|
|
((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2014-03-26 21:17:17 +04:00
|
|
|
/* Reopen MNTTAB to prevent reading stale data from open file */
|
|
|
|
if (freopen(MNTTAB, "r", mnttab_file) == NULL)
|
|
|
|
return (ENOENT);
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
while (getmntent(mnttab_file, &entry) == 0) {
|
|
|
|
|
|
|
|
/* ignore non-ZFS entries */
|
|
|
|
if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* ignore snapshots */
|
|
|
|
if (strchr(entry.mnt_special, '@') != NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((zhp = zfs_open(g_zfs, entry.mnt_special,
|
|
|
|
ZFS_TYPE_FILESYSTEM)) == NULL) {
|
|
|
|
ret = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-02-04 00:24:44 +03:00
|
|
|
/*
|
|
|
|
* Ignore datasets that are excluded/restricted by
|
|
|
|
* parent pool name.
|
|
|
|
*/
|
|
|
|
if (zpool_skip_pool(zfs_get_pool_name(zhp))) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (op) {
|
|
|
|
case OP_SHARE:
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
|
2010-05-29 00:45:14 +04:00
|
|
|
nfs_mnt_prop,
|
|
|
|
sizeof (nfs_mnt_prop),
|
2008-11-20 23:01:55 +03:00
|
|
|
NULL, NULL, 0, B_FALSE) == 0);
|
2010-05-29 00:45:14 +04:00
|
|
|
if (strcmp(nfs_mnt_prop, "off") != 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
break;
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
|
2010-05-29 00:45:14 +04:00
|
|
|
nfs_mnt_prop,
|
|
|
|
sizeof (nfs_mnt_prop),
|
2008-11-20 23:01:55 +03:00
|
|
|
NULL, NULL, 0, B_FALSE) == 0);
|
2010-05-29 00:45:14 +04:00
|
|
|
if (strcmp(nfs_mnt_prop, "off") == 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case OP_MOUNT:
|
|
|
|
/* Ignore legacy mounts */
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
|
2010-05-29 00:45:14 +04:00
|
|
|
nfs_mnt_prop,
|
|
|
|
sizeof (nfs_mnt_prop),
|
2008-11-20 23:01:55 +03:00
|
|
|
NULL, NULL, 0, B_FALSE) == 0);
|
2010-05-29 00:45:14 +04:00
|
|
|
if (strcmp(nfs_mnt_prop, "legacy") == 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
continue;
|
|
|
|
/* Ignore canmount=noauto mounts */
|
|
|
|
if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
|
|
|
|
ZFS_CANMOUNT_NOAUTO)
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = safe_malloc(sizeof (unshare_unmount_node_t));
|
|
|
|
node->un_zhp = zhp;
|
2010-05-29 00:45:14 +04:00
|
|
|
node->un_mountp = safe_strdup(entry.mnt_mountp);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
uu_avl_node_init(node, &node->un_avlnode, pool);
|
|
|
|
|
|
|
|
if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
|
|
|
|
uu_avl_insert(tree, node, idx);
|
|
|
|
} else {
|
|
|
|
zfs_close(node->un_zhp);
|
|
|
|
free(node->un_mountp);
|
|
|
|
free(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the AVL tree in reverse, unmounting each filesystem and
|
|
|
|
* removing it from the AVL tree in the process.
|
|
|
|
*/
|
|
|
|
if ((walk = uu_avl_walk_start(tree,
|
2010-05-29 00:45:14 +04:00
|
|
|
UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
|
|
|
|
nomem();
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
while ((node = uu_avl_walk_next(walk)) != NULL) {
|
2019-12-11 22:58:37 +03:00
|
|
|
const char *mntarg = NULL;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2019-12-11 22:58:37 +03:00
|
|
|
uu_avl_remove(tree, node);
|
|
|
|
#ifndef __FreeBSD__
|
|
|
|
mntarg = node->un_zhp->zfs_name;
|
|
|
|
#endif
|
2008-11-20 23:01:55 +03:00
|
|
|
switch (op) {
|
|
|
|
case OP_SHARE:
|
2016-11-29 22:22:38 +03:00
|
|
|
if (zfs_unshareall_bytype(node->un_zhp,
|
|
|
|
node->un_mountp, protocol) != 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_MOUNT:
|
|
|
|
if (zfs_unmount(node->un_zhp,
|
2019-12-11 22:58:37 +03:00
|
|
|
mntarg, flags) != 0)
|
2008-11-20 23:01:55 +03:00
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
zfs_close(node->un_zhp);
|
|
|
|
free(node->un_mountp);
|
|
|
|
free(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
uu_avl_walk_end(walk);
|
|
|
|
uu_avl_destroy(tree);
|
|
|
|
uu_avl_pool_destroy(pool);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (argc != 1) {
|
|
|
|
if (argc == 0)
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("missing filesystem argument\n"));
|
|
|
|
else
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have an argument, but it may be a full path or a ZFS
|
|
|
|
* filesystem. Pass full paths off to unmount_path() (shared by
|
|
|
|
* manual_unmount), otherwise open the filesystem and pass to
|
|
|
|
* zfs_unmount().
|
|
|
|
*/
|
|
|
|
if (argv[0][0] == '/')
|
|
|
|
return (unshare_unmount_path(op, argv[0],
|
|
|
|
flags, B_FALSE));
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
if ((zhp = zfs_open(g_zfs, argv[0],
|
|
|
|
ZFS_TYPE_FILESYSTEM)) == NULL)
|
2008-11-20 23:01:55 +03:00
|
|
|
return (1);
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
verify(zfs_prop_get(zhp, op == OP_SHARE ?
|
|
|
|
ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
|
|
|
|
nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
|
|
|
|
NULL, 0, B_FALSE) == 0);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
switch (op) {
|
|
|
|
case OP_SHARE:
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
|
|
|
|
nfs_mnt_prop,
|
|
|
|
sizeof (nfs_mnt_prop),
|
|
|
|
NULL, NULL, 0, B_FALSE) == 0);
|
|
|
|
verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
|
|
|
|
sharesmb, sizeof (sharesmb), NULL, NULL,
|
|
|
|
0, B_FALSE) == 0);
|
|
|
|
|
|
|
|
if (strcmp(nfs_mnt_prop, "off") == 0 &&
|
|
|
|
strcmp(sharesmb, "off") == 0) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"unshare '%s': legacy share\n"),
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
(void) fprintf(stderr, gettext("use "
|
|
|
|
"unshare(1M) to unshare this "
|
|
|
|
"filesystem\n"));
|
|
|
|
ret = 1;
|
|
|
|
} else if (!zfs_is_shared(zhp)) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"unshare '%s': not currently "
|
|
|
|
"shared\n"), zfs_get_name(zhp));
|
|
|
|
ret = 1;
|
|
|
|
} else if (zfs_unshareall(zhp) != 0) {
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
case OP_MOUNT:
|
|
|
|
if (strcmp(nfs_mnt_prop, "legacy") == 0) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"unmount '%s': legacy "
|
|
|
|
"mountpoint\n"), zfs_get_name(zhp));
|
|
|
|
(void) fprintf(stderr, gettext("use "
|
|
|
|
"umount(1M) to unmount this "
|
|
|
|
"filesystem\n"));
|
|
|
|
ret = 1;
|
|
|
|
} else if (!zfs_is_mounted(zhp, NULL)) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"unmount '%s': not currently "
|
|
|
|
"mounted\n"),
|
|
|
|
zfs_get_name(zhp));
|
|
|
|
ret = 1;
|
|
|
|
} else if (zfs_unmountall(zhp, flags) != 0) {
|
|
|
|
ret = 1;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
2010-08-27 01:24:34 +04:00
|
|
|
break;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-06-28 22:38:37 +03:00
|
|
|
* zfs unmount [-fu] -a
|
|
|
|
* zfs unmount [-fu] filesystem
|
2008-11-20 23:01:55 +03:00
|
|
|
*
|
|
|
|
* Unmount all filesystems, or a specific ZFS filesystem.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_unmount(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (unshare_unmount(OP_MOUNT, argc, argv));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* zfs unshare -a
|
|
|
|
* zfs unshare filesystem
|
|
|
|
*
|
|
|
|
* Unshare all filesystems, or a specific ZFS filesystem.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_unshare(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (unshare_unmount(OP_SHARE, argc, argv));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
find_command_idx(char *command, int *idx)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NCOMMAND; i++) {
|
|
|
|
if (command_table[i].name == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (strcmp(command, command_table[i].name) == 0) {
|
|
|
|
*idx = i;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2010-08-27 01:24:34 +04:00
|
|
|
static int
|
|
|
|
zfs_do_diff(int argc, char **argv)
|
|
|
|
{
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
int flags = 0;
|
|
|
|
char *tosnap = NULL;
|
|
|
|
char *fromsnap = NULL;
|
|
|
|
char *atp, *copy;
|
2012-04-24 14:51:02 +04:00
|
|
|
int err = 0;
|
2010-08-27 01:24:34 +04:00
|
|
|
int c;
|
2019-04-09 19:58:28 +03:00
|
|
|
struct sigaction sa;
|
2010-08-27 01:24:34 +04:00
|
|
|
|
|
|
|
while ((c = getopt(argc, argv, "FHt")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'F':
|
|
|
|
flags |= ZFS_DIFF_CLASSIFY;
|
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
flags |= ZFS_DIFF_PARSEABLE;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
flags |= ZFS_DIFF_TIMESTAMP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option '%c'\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr,
|
OpenZFS 7614, 9064 - zfs device evacuation/removal
OpenZFS 7614 - zfs device evacuation/removal
OpenZFS 9064 - remove_mirror should wait for device removal to complete
This project allows top-level vdevs to be removed from the storage pool
with "zpool remove", reducing the total amount of storage in the pool.
This operation copies all allocated regions of the device to be removed
onto other devices, recording the mapping from old to new location.
After the removal is complete, read and free operations to the removed
(now "indirect") vdev must be remapped and performed at the new location
on disk. The indirect mapping table is kept in memory whenever the pool
is loaded, so there is minimal performance overhead when doing operations
on the indirect vdev.
The size of the in-memory mapping table will be reduced when its entries
become "obsolete" because they are no longer used by any block pointers
in the pool. An entry becomes obsolete when all the blocks that use
it are freed. An entry can also become obsolete when all the snapshots
that reference it are deleted, and the block pointers that reference it
have been "remapped" in all filesystems/zvols (and clones). Whenever an
indirect block is written, all the block pointers in it will be "remapped"
to their new (concrete) locations if possible. This process can be
accelerated by using the "zfs remap" command to proactively rewrite all
indirect blocks that reference indirect (removed) vdevs.
Note that when a device is removed, we do not verify the checksum of
the data that is copied. This makes the process much faster, but if it
were used on redundant vdevs (i.e. mirror or raidz vdevs), it would be
possible to copy the wrong data, when we have the correct data on e.g.
the other side of the mirror.
At the moment, only mirrors and simple top-level vdevs can be removed
and no removal is allowed if any of the top-level vdevs are raidz.
Porting Notes:
* Avoid zero-sized kmem_alloc() in vdev_compact_children().
The device evacuation code adds a dependency that
vdev_compact_children() be able to properly empty the vdev_child
array by setting it to NULL and zeroing vdev_children. Under Linux,
kmem_alloc() and related functions return a sentinel pointer rather
than NULL for zero-sized allocations.
* Remove comment regarding "mpt" driver where zfs_remove_max_segment
is initialized to SPA_MAXBLOCKSIZE.
Change zfs_condense_indirect_commit_entry_delay_ticks to
zfs_condense_indirect_commit_entry_delay_ms for consistency with
most other tunables in which delays are specified in ms.
* ZTS changes:
Use set_tunable rather than mdb
Use zpool sync as appropriate
Use sync_pool instead of sync
Kill jobs during test_removal_with_operation to allow unmount/export
Don't add non-disk names such as "mirror" or "raidz" to $DISKS
Use $TEST_BASE_DIR instead of /tmp
Increase HZ from 100 to 1000 which is more common on Linux
removal_multiple_indirection.ksh
Reduce iterations in order to not time out on the code
coverage builders.
removal_resume_export:
Functionally, the test case is correct but there exists a race
where the kernel thread hasn't been fully started yet and is
not visible. Wait for up to 1 second for the removal thread
to be started before giving up on it. Also, increase the
amount of data copied in order that the removal not finish
before the export has a chance to fail.
* MMP compatibility, the concept of concrete versus non-concrete devices
has slightly changed the semantics of vdev_writeable(). Update
mmp_random_leaf_impl() accordingly.
* Updated dbuf_remap() to handle the org.zfsonlinux:large_dnode pool
feature which is not supported by OpenZFS.
* Added support for new vdev removal tracepoints.
* Test cases removal_with_zdb and removal_condense_export have been
intentionally disabled. When run manually they pass as intended,
but when running in the automated test environment they produce
unreliable results on the latest Fedora release.
They may work better once the upstream pool import refectoring is
merged into ZoL at which point they will be re-enabled.
Authored by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Alex Reece <alex@delphix.com>
Reviewed-by: George Wilson <george.wilson@delphix.com>
Reviewed-by: John Kennedy <john.kennedy@delphix.com>
Reviewed-by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Richard Laager <rlaager@wiktel.com>
Reviewed by: Tim Chase <tim@chase2k.com>
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Approved by: Garrett D'Amore <garrett@damore.org>
Ported-by: Tim Chase <tim@chase2k.com>
Signed-off-by: Tim Chase <tim@chase2k.com>
OpenZFS-issue: https://www.illumos.org/issues/7614
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/f539f1eb
Closes #6900
2016-09-22 19:30:13 +03:00
|
|
|
gettext("must provide at least one snapshot name\n"));
|
2010-08-27 01:24:34 +04:00
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > 2) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
fromsnap = argv[0];
|
|
|
|
tosnap = (argc == 2) ? argv[1] : NULL;
|
|
|
|
|
|
|
|
copy = NULL;
|
|
|
|
if (*fromsnap != '@')
|
|
|
|
copy = strdup(fromsnap);
|
|
|
|
else if (tosnap)
|
|
|
|
copy = strdup(tosnap);
|
|
|
|
if (copy == NULL)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
2017-01-18 01:45:02 +03:00
|
|
|
if ((atp = strchr(copy, '@')) != NULL)
|
2010-08-27 01:24:34 +04:00
|
|
|
*atp = '\0';
|
|
|
|
|
2016-09-29 22:11:44 +03:00
|
|
|
if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) {
|
|
|
|
free(copy);
|
2010-08-27 01:24:34 +04:00
|
|
|
return (1);
|
2016-09-29 22:11:44 +03:00
|
|
|
}
|
2010-08-27 01:24:34 +04:00
|
|
|
free(copy);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore SIGPIPE so that the library can give us
|
|
|
|
* information on any failure
|
|
|
|
*/
|
2019-04-09 19:58:28 +03:00
|
|
|
if (sigemptyset(&sa.sa_mask) == -1) {
|
|
|
|
err = errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
sa.sa_flags = 0;
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
|
|
if (sigaction(SIGPIPE, &sa, NULL) == -1) {
|
|
|
|
err = errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-08-27 01:24:34 +04:00
|
|
|
|
|
|
|
err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
|
2019-04-09 19:58:28 +03:00
|
|
|
out:
|
2010-08-27 01:24:34 +04:00
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (err != 0);
|
|
|
|
}
|
|
|
|
|
2013-12-12 02:33:41 +04:00
|
|
|
/*
|
2019-11-11 10:24:14 +03:00
|
|
|
* zfs bookmark <fs@source>|<fs#source> <fs#bookmark>
|
2013-12-12 02:33:41 +04:00
|
|
|
*
|
2019-11-11 10:24:14 +03:00
|
|
|
* Creates a bookmark with the given name from the source snapshot
|
|
|
|
* or creates a copy of an existing source bookmark.
|
2013-12-12 02:33:41 +04:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_bookmark(int argc, char **argv)
|
|
|
|
{
|
2019-11-11 10:24:14 +03:00
|
|
|
char *source, *bookname;
|
|
|
|
char expbuf[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
int source_type;
|
2013-12-12 02:33:41 +04:00
|
|
|
nvlist_t *nvl;
|
|
|
|
int ret = 0;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
/* check options */
|
|
|
|
while ((c = getopt(argc, argv, "")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option '%c'\n"), optopt);
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 1) {
|
2019-11-11 10:24:14 +03:00
|
|
|
(void) fprintf(stderr, gettext("missing source argument\n"));
|
2013-12-12 02:33:41 +04:00
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
if (argc < 2) {
|
|
|
|
(void) fprintf(stderr, gettext("missing bookmark argument\n"));
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
|
2019-11-11 10:24:14 +03:00
|
|
|
source = argv[0];
|
|
|
|
bookname = argv[1];
|
|
|
|
|
|
|
|
if (strchr(source, '@') == NULL && strchr(source, '#') == NULL) {
|
2018-02-26 20:55:18 +03:00
|
|
|
(void) fprintf(stderr,
|
2019-11-11 10:24:14 +03:00
|
|
|
gettext("invalid source name '%s': "
|
|
|
|
"must contain a '@' or '#'\n"), source);
|
2018-02-26 20:55:18 +03:00
|
|
|
goto usage;
|
|
|
|
}
|
2019-11-11 10:24:14 +03:00
|
|
|
if (strchr(bookname, '#') == NULL) {
|
2013-12-12 02:33:41 +04:00
|
|
|
(void) fprintf(stderr,
|
2017-04-20 22:09:40 +03:00
|
|
|
gettext("invalid bookmark name '%s': "
|
2019-11-11 10:24:14 +03:00
|
|
|
"must contain a '#'\n"), bookname);
|
2013-12-12 02:33:41 +04:00
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
|
2019-11-11 10:24:14 +03:00
|
|
|
/*
|
|
|
|
* expand source or bookname to full path:
|
|
|
|
* one of them may be specified as short name
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
char **expand;
|
|
|
|
char *source_short, *bookname_short;
|
|
|
|
source_short = strpbrk(source, "@#");
|
|
|
|
bookname_short = strpbrk(bookname, "#");
|
|
|
|
if (source_short == source &&
|
|
|
|
bookname_short == bookname) {
|
|
|
|
(void) fprintf(stderr, gettext(
|
|
|
|
"either source or bookmark must be specified as "
|
|
|
|
"full dataset paths"));
|
|
|
|
goto usage;
|
|
|
|
} else if (source_short != source &&
|
|
|
|
bookname_short != bookname) {
|
|
|
|
expand = NULL;
|
|
|
|
} else if (source_short != source) {
|
|
|
|
strlcpy(expbuf, source, sizeof (expbuf));
|
|
|
|
expand = &bookname;
|
|
|
|
} else if (bookname_short != bookname) {
|
|
|
|
strlcpy(expbuf, bookname, sizeof (expbuf));
|
|
|
|
expand = &source;
|
|
|
|
} else {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
if (expand != NULL) {
|
|
|
|
*strpbrk(expbuf, "@#") = '\0'; /* dataset name in buf */
|
|
|
|
(void) strlcat(expbuf, *expand, sizeof (expbuf));
|
|
|
|
*expand = expbuf;
|
|
|
|
}
|
2013-12-12 02:33:41 +04:00
|
|
|
}
|
2019-11-11 10:24:14 +03:00
|
|
|
|
|
|
|
/* determine source type */
|
|
|
|
switch (*strpbrk(source, "@#")) {
|
|
|
|
case '@': source_type = ZFS_TYPE_SNAPSHOT; break;
|
|
|
|
case '#': source_type = ZFS_TYPE_BOOKMARK; break;
|
|
|
|
default: abort();
|
2017-04-20 22:09:40 +03:00
|
|
|
}
|
|
|
|
|
2019-11-11 10:24:14 +03:00
|
|
|
/* test the source exists */
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
zhp = zfs_open(g_zfs, source, source_type);
|
2013-12-12 02:33:41 +04:00
|
|
|
if (zhp == NULL)
|
|
|
|
goto usage;
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
nvl = fnvlist_alloc();
|
2019-11-11 10:24:14 +03:00
|
|
|
fnvlist_add_string(nvl, bookname, source);
|
2013-12-12 02:33:41 +04:00
|
|
|
ret = lzc_bookmark(nvl, NULL);
|
|
|
|
fnvlist_free(nvl);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
const char *err_msg = NULL;
|
2013-12-12 02:33:41 +04:00
|
|
|
char errbuf[1024];
|
|
|
|
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf),
|
|
|
|
dgettext(TEXT_DOMAIN,
|
2017-04-20 22:09:40 +03:00
|
|
|
"cannot create bookmark '%s'"), bookname);
|
2013-12-12 02:33:41 +04:00
|
|
|
|
|
|
|
switch (ret) {
|
|
|
|
case EXDEV:
|
|
|
|
err_msg = "bookmark is in a different pool";
|
|
|
|
break;
|
2019-11-11 10:24:14 +03:00
|
|
|
case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:
|
|
|
|
err_msg = "source is not an ancestor of the "
|
|
|
|
"new bookmark's dataset";
|
|
|
|
break;
|
2013-12-12 02:33:41 +04:00
|
|
|
case EEXIST:
|
|
|
|
err_msg = "bookmark exists";
|
|
|
|
break;
|
|
|
|
case EINVAL:
|
|
|
|
err_msg = "invalid argument";
|
|
|
|
break;
|
|
|
|
case ENOTSUP:
|
|
|
|
err_msg = "bookmark feature not enabled";
|
|
|
|
break;
|
2014-11-03 23:28:43 +03:00
|
|
|
case ENOSPC:
|
|
|
|
err_msg = "out of space";
|
|
|
|
break;
|
2017-04-20 22:09:40 +03:00
|
|
|
case ENOENT:
|
|
|
|
err_msg = "dataset does not exist";
|
|
|
|
break;
|
2013-12-12 02:33:41 +04:00
|
|
|
default:
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
(void) zfs_standard_error(g_zfs, ret, errbuf);
|
2013-12-12 02:33:41 +04:00
|
|
|
break;
|
|
|
|
}
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
if (err_msg != NULL) {
|
|
|
|
(void) fprintf(stderr, "%s: %s\n", errbuf,
|
|
|
|
dgettext(TEXT_DOMAIN, err_msg));
|
|
|
|
}
|
2013-12-12 02:33:41 +04:00
|
|
|
}
|
|
|
|
|
2014-11-03 23:28:43 +03:00
|
|
|
return (ret != 0);
|
2013-12-12 02:33:41 +04:00
|
|
|
|
|
|
|
usage:
|
|
|
|
usage(B_FALSE);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2018-02-08 19:16:23 +03:00
|
|
|
static int
|
|
|
|
zfs_do_channel_program(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int ret, fd, c;
|
|
|
|
char *progbuf, *filename, *poolname;
|
|
|
|
size_t progsize, progread;
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
nvlist_t *outnvl = NULL;
|
2018-02-08 19:16:23 +03:00
|
|
|
uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
|
|
|
|
uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
|
2018-03-19 22:40:58 +03:00
|
|
|
boolean_t sync_flag = B_TRUE, json_output = B_FALSE;
|
2018-02-08 19:16:23 +03:00
|
|
|
zpool_handle_t *zhp;
|
|
|
|
|
|
|
|
/* check options */
|
2018-03-19 22:40:58 +03:00
|
|
|
while ((c = getopt(argc, argv, "nt:m:j")) != -1) {
|
2018-02-08 19:16:23 +03:00
|
|
|
switch (c) {
|
|
|
|
case 't':
|
|
|
|
case 'm': {
|
|
|
|
uint64_t arg;
|
|
|
|
char *endp;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
arg = strtoull(optarg, &endp, 0);
|
|
|
|
if (errno != 0 || *endp != '\0') {
|
|
|
|
(void) fprintf(stderr, gettext(
|
|
|
|
"invalid argument "
|
|
|
|
"'%s': expected integer\n"), optarg);
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == 't') {
|
2018-06-16 01:10:42 +03:00
|
|
|
instrlimit = arg;
|
2018-02-08 19:16:23 +03:00
|
|
|
} else {
|
|
|
|
ASSERT3U(c, ==, 'm');
|
2018-06-16 01:10:42 +03:00
|
|
|
memlimit = arg;
|
2018-02-08 19:16:23 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-02-08 19:35:09 +03:00
|
|
|
case 'n': {
|
|
|
|
sync_flag = B_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
2018-03-19 22:40:58 +03:00
|
|
|
case 'j': {
|
|
|
|
json_output = B_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2018-02-08 19:16:23 +03:00
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (argc < 2) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid number of arguments\n"));
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
|
|
|
|
poolname = argv[0];
|
|
|
|
filename = argv[1];
|
|
|
|
if (strcmp(filename, "-") == 0) {
|
|
|
|
fd = 0;
|
|
|
|
filename = "standard input";
|
|
|
|
} else if ((fd = open(filename, O_RDONLY)) < 0) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
|
|
|
|
filename, strerror(errno));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {
|
2019-05-24 23:54:36 +03:00
|
|
|
(void) fprintf(stderr, gettext("cannot open pool '%s'\n"),
|
2018-02-08 19:16:23 +03:00
|
|
|
poolname);
|
2018-02-20 22:19:42 +03:00
|
|
|
if (fd != 0)
|
|
|
|
(void) close(fd);
|
2018-02-08 19:16:23 +03:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
zpool_close(zhp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read in the channel program, expanding the program buffer as
|
|
|
|
* necessary.
|
|
|
|
*/
|
|
|
|
progread = 0;
|
|
|
|
progsize = 1024;
|
|
|
|
progbuf = safe_malloc(progsize);
|
|
|
|
do {
|
|
|
|
ret = read(fd, progbuf + progread, progsize - progread);
|
|
|
|
progread += ret;
|
|
|
|
if (progread == progsize && ret > 0) {
|
|
|
|
progsize *= 2;
|
|
|
|
progbuf = safe_realloc(progbuf, progsize);
|
|
|
|
}
|
|
|
|
} while (ret > 0);
|
|
|
|
|
|
|
|
if (fd != 0)
|
|
|
|
(void) close(fd);
|
|
|
|
if (ret < 0) {
|
|
|
|
free(progbuf);
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("cannot read '%s': %s\n"),
|
|
|
|
filename, strerror(errno));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
progbuf[progread] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Any remaining arguments are passed as arguments to the lua script as
|
|
|
|
* a string array:
|
|
|
|
* {
|
|
|
|
* "argv" -> [ "arg 1", ... "arg n" ],
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
nvlist_t *argnvl = fnvlist_alloc();
|
|
|
|
fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2);
|
|
|
|
|
2018-02-08 19:35:09 +03:00
|
|
|
if (sync_flag) {
|
|
|
|
ret = lzc_channel_program(poolname, progbuf,
|
|
|
|
instrlimit, memlimit, argnvl, &outnvl);
|
|
|
|
} else {
|
|
|
|
ret = lzc_channel_program_nosync(poolname, progbuf,
|
|
|
|
instrlimit, memlimit, argnvl, &outnvl);
|
|
|
|
}
|
2018-02-08 19:16:23 +03:00
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
/*
|
|
|
|
* On error, report the error message handed back by lua if one
|
|
|
|
* exists. Otherwise, generate an appropriate error message,
|
|
|
|
* falling back on strerror() for an unexpected return code.
|
|
|
|
*/
|
|
|
|
char *errstring = NULL;
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
const char *msg = gettext("Channel program execution failed");
|
2018-02-08 19:16:23 +03:00
|
|
|
uint64_t instructions = 0;
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {
|
2018-02-08 19:16:23 +03:00
|
|
|
(void) nvlist_lookup_string(outnvl,
|
|
|
|
ZCP_RET_ERROR, &errstring);
|
|
|
|
if (errstring == NULL)
|
|
|
|
errstring = strerror(ret);
|
|
|
|
if (ret == ETIME) {
|
|
|
|
(void) nvlist_lookup_uint64(outnvl,
|
|
|
|
ZCP_ARG_INSTRLIMIT, &instructions);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (ret) {
|
|
|
|
case EINVAL:
|
|
|
|
errstring =
|
|
|
|
"Invalid instruction or memory limit.";
|
|
|
|
break;
|
|
|
|
case ENOMEM:
|
|
|
|
errstring = "Return value too large.";
|
|
|
|
break;
|
|
|
|
case ENOSPC:
|
|
|
|
errstring = "Memory limit exhausted.";
|
|
|
|
break;
|
|
|
|
case ETIME:
|
|
|
|
errstring = "Timed out.";
|
|
|
|
break;
|
|
|
|
case EPERM:
|
|
|
|
errstring = "Permission denied. Channel "
|
|
|
|
"programs must be run as root.";
|
|
|
|
break;
|
|
|
|
default:
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
(void) zfs_standard_error(g_zfs, ret, msg);
|
2018-02-08 19:16:23 +03:00
|
|
|
}
|
|
|
|
}
|
Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel
ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
2018-09-02 22:14:01 +03:00
|
|
|
if (errstring != NULL)
|
|
|
|
(void) fprintf(stderr, "%s:\n%s\n", msg, errstring);
|
|
|
|
|
2018-02-08 19:16:23 +03:00
|
|
|
if (ret == ETIME && instructions != 0)
|
2018-03-19 22:40:58 +03:00
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("%llu Lua instructions\n"),
|
2018-02-08 19:16:23 +03:00
|
|
|
(u_longlong_t)instructions);
|
|
|
|
} else {
|
2018-03-19 22:40:58 +03:00
|
|
|
if (json_output) {
|
|
|
|
(void) nvlist_print_json(stdout, outnvl);
|
|
|
|
} else if (nvlist_empty(outnvl)) {
|
|
|
|
(void) fprintf(stdout, gettext("Channel program fully "
|
|
|
|
"executed and did not produce output.\n"));
|
2018-02-08 19:16:23 +03:00
|
|
|
} else {
|
2018-03-19 22:40:58 +03:00
|
|
|
(void) fprintf(stdout, gettext("Channel program fully "
|
|
|
|
"executed and produced output:\n"));
|
2018-02-08 19:16:23 +03:00
|
|
|
dump_nvlist(outnvl, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(progbuf);
|
|
|
|
fnvlist_free(outnvl);
|
|
|
|
fnvlist_free(argnvl);
|
|
|
|
return (ret != 0);
|
|
|
|
|
|
|
|
usage:
|
|
|
|
usage(B_FALSE);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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 20:36:48 +03:00
|
|
|
typedef struct loadkey_cbdata {
|
|
|
|
boolean_t cb_loadkey;
|
|
|
|
boolean_t cb_recursive;
|
|
|
|
boolean_t cb_noop;
|
|
|
|
char *cb_keylocation;
|
|
|
|
uint64_t cb_numfailed;
|
|
|
|
uint64_t cb_numattempted;
|
|
|
|
} loadkey_cbdata_t;
|
|
|
|
|
|
|
|
static int
|
|
|
|
load_key_callback(zfs_handle_t *zhp, void *data)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
boolean_t is_encroot;
|
|
|
|
loadkey_cbdata_t *cb = data;
|
|
|
|
uint64_t keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are working recursively, we want to skip loading / unloading
|
|
|
|
* keys for non-encryption roots and datasets whose keys are already
|
|
|
|
* in the desired end-state.
|
|
|
|
*/
|
|
|
|
if (cb->cb_recursive) {
|
|
|
|
ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);
|
|
|
|
if (ret != 0)
|
|
|
|
return (ret);
|
|
|
|
if (!is_encroot)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if ((cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_AVAILABLE) ||
|
|
|
|
(!cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_UNAVAILABLE))
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
cb->cb_numattempted++;
|
|
|
|
|
|
|
|
if (cb->cb_loadkey)
|
|
|
|
ret = zfs_crypto_load_key(zhp, cb->cb_noop, cb->cb_keylocation);
|
|
|
|
else
|
|
|
|
ret = zfs_crypto_unload_key(zhp);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
cb->cb_numfailed++;
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
load_unload_keys(int argc, char **argv, boolean_t loadkey)
|
|
|
|
{
|
|
|
|
int c, ret = 0, flags = 0;
|
|
|
|
boolean_t do_all = B_FALSE;
|
|
|
|
loadkey_cbdata_t cb = { 0 };
|
|
|
|
|
|
|
|
cb.cb_loadkey = loadkey;
|
|
|
|
|
|
|
|
while ((c = getopt(argc, argv, "anrL:")) != -1) {
|
|
|
|
/* noop and alternate keylocations only apply to zfs load-key */
|
|
|
|
if (loadkey) {
|
|
|
|
switch (c) {
|
|
|
|
case 'n':
|
|
|
|
cb.cb_noop = B_TRUE;
|
|
|
|
continue;
|
|
|
|
case 'L':
|
|
|
|
cb.cb_keylocation = optarg;
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case 'a':
|
|
|
|
do_all = B_TRUE;
|
|
|
|
cb.cb_recursive = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
flags |= ZFS_ITER_RECURSE;
|
|
|
|
cb.cb_recursive = B_TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option '%c'\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (!do_all && argc == 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Missing dataset argument or -a option\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_all && argc != 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Cannot specify dataset with -a option\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb.cb_recursive && cb.cb_keylocation != NULL &&
|
|
|
|
strcmp(cb.cb_keylocation, "prompt") != 0) {
|
|
|
|
(void) fprintf(stderr, gettext("alternate keylocation may only "
|
|
|
|
"be 'prompt' with -r or -a\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = zfs_for_each(argc, argv, flags,
|
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 0,
|
|
|
|
load_key_callback, &cb);
|
|
|
|
|
|
|
|
if (cb.cb_noop || (cb.cb_recursive && cb.cb_numattempted != 0)) {
|
|
|
|
(void) printf(gettext("%llu / %llu key(s) successfully %s\n"),
|
|
|
|
(u_longlong_t)(cb.cb_numattempted - cb.cb_numfailed),
|
|
|
|
(u_longlong_t)cb.cb_numattempted,
|
|
|
|
loadkey ? (cb.cb_noop ? "verified" : "loaded") :
|
|
|
|
"unloaded");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb.cb_numfailed != 0)
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_load_key(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (load_unload_keys(argc, argv, B_TRUE));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_unload_key(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (load_unload_keys(argc, argv, B_FALSE));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_do_change_key(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int c, ret;
|
|
|
|
uint64_t keystatus;
|
|
|
|
boolean_t loadkey = B_FALSE, inheritkey = B_FALSE;
|
|
|
|
zfs_handle_t *zhp = NULL;
|
|
|
|
nvlist_t *props = fnvlist_alloc();
|
|
|
|
|
|
|
|
while ((c = getopt(argc, argv, "lio:")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 'l':
|
|
|
|
loadkey = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
inheritkey = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
if (!parseprop(props, optarg)) {
|
|
|
|
nvlist_free(props);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid option '%c'\n"), optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inheritkey && !nvlist_empty(props)) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("Properties not allowed for inheriting\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("Missing dataset argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("Too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
zhp = zfs_open(g_zfs, argv[argc - 1],
|
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
|
|
|
if (loadkey) {
|
|
|
|
keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
|
|
|
|
if (keystatus != ZFS_KEYSTATUS_AVAILABLE) {
|
|
|
|
ret = zfs_crypto_load_key(zhp, B_FALSE, NULL);
|
2017-09-12 23:15:11 +03:00
|
|
|
if (ret != 0) {
|
|
|
|
nvlist_free(props);
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (-1);
|
|
|
|
}
|
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 20:36:48 +03:00
|
|
|
}
|
|
|
|
|
2017-09-12 23:15:11 +03:00
|
|
|
/* refresh the properties so the new keystatus is visible */
|
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 20:36:48 +03:00
|
|
|
zfs_refresh_properties(zhp);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = zfs_crypto_rewrap(zhp, props, inheritkey);
|
2017-09-12 23:15:11 +03:00
|
|
|
if (ret != 0) {
|
|
|
|
nvlist_free(props);
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (-1);
|
|
|
|
}
|
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 20:36:48 +03:00
|
|
|
|
|
|
|
nvlist_free(props);
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2018-02-14 01:54:54 +03:00
|
|
|
/*
|
|
|
|
* 1) zfs project [-d|-r] <file|directory ...>
|
|
|
|
* List project ID and inherit flag of file(s) or directories.
|
|
|
|
* -d: List the directory itself, not its children.
|
|
|
|
* -r: List subdirectories recursively.
|
|
|
|
*
|
|
|
|
* 2) zfs project -C [-k] [-r] <file|directory ...>
|
|
|
|
* Clear project inherit flag and/or ID on the file(s) or directories.
|
|
|
|
* -k: Keep the project ID unchanged. If not specified, the project ID
|
|
|
|
* will be reset as zero.
|
|
|
|
* -r: Clear on subdirectories recursively.
|
|
|
|
*
|
|
|
|
* 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...>
|
|
|
|
* Check project ID and inherit flag on the file(s) or directories,
|
|
|
|
* report the outliers.
|
|
|
|
* -0: Print file name followed by a NUL instead of newline.
|
|
|
|
* -d: Check the directory itself, not its children.
|
|
|
|
* -p: Specify the referenced ID for comparing with the target file(s)
|
|
|
|
* or directories' project IDs. If not specified, the target (top)
|
|
|
|
* directory's project ID will be used as the referenced one.
|
|
|
|
* -r: Check subdirectories recursively.
|
|
|
|
*
|
|
|
|
* 4) zfs project [-p id] [-r] [-s] <file|directory ...>
|
|
|
|
* Set project ID and/or inherit flag on the file(s) or directories.
|
|
|
|
* -p: Set the project ID as the given id.
|
2019-08-30 19:43:30 +03:00
|
|
|
* -r: Set on subdirectories recursively. If not specify "-p" option,
|
2018-02-14 01:54:54 +03:00
|
|
|
* it will use top-level directory's project ID as the given id,
|
|
|
|
* then set both project ID and inherit flag on all descendants
|
|
|
|
* of the top-level directory.
|
|
|
|
* -s: Set project inherit flag.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_project(int argc, char **argv)
|
|
|
|
{
|
|
|
|
zfs_project_control_t zpc = {
|
|
|
|
.zpc_expected_projid = ZFS_INVALID_PROJID,
|
|
|
|
.zpc_op = ZFS_PROJECT_OP_DEFAULT,
|
|
|
|
.zpc_dironly = B_FALSE,
|
|
|
|
.zpc_keep_projid = B_FALSE,
|
|
|
|
.zpc_newline = B_TRUE,
|
|
|
|
.zpc_recursive = B_FALSE,
|
|
|
|
.zpc_set_flag = B_FALSE,
|
|
|
|
};
|
|
|
|
int ret = 0, c;
|
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
usage(B_FALSE);
|
|
|
|
|
|
|
|
while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case '0':
|
|
|
|
zpc.zpc_newline = B_FALSE;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"specify '-C' '-c' '-s' together\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
zpc.zpc_op = ZFS_PROJECT_OP_CLEAR;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"specify '-C' '-c' '-s' together\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
zpc.zpc_op = ZFS_PROJECT_OP_CHECK;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
zpc.zpc_dironly = B_TRUE;
|
|
|
|
/* overwrite "-r" option */
|
|
|
|
zpc.zpc_recursive = B_FALSE;
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
zpc.zpc_keep_projid = B_TRUE;
|
|
|
|
break;
|
|
|
|
case 'p': {
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0);
|
|
|
|
if (errno != 0 || *endptr != '\0') {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("project ID must be less than "
|
|
|
|
"%u\n"), UINT32_MAX);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (zpc.zpc_expected_projid >= UINT32_MAX) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid project ID\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'r':
|
|
|
|
zpc.zpc_recursive = B_TRUE;
|
|
|
|
/* overwrite "-d" option */
|
|
|
|
zpc.zpc_dironly = B_FALSE;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
|
|
|
|
(void) fprintf(stderr, gettext("cannot "
|
|
|
|
"specify '-C' '-c' '-s' together\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
zpc.zpc_set_flag = B_TRUE;
|
|
|
|
zpc.zpc_op = ZFS_PROJECT_OP_SET;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) {
|
|
|
|
if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID)
|
|
|
|
zpc.zpc_op = ZFS_PROJECT_OP_SET;
|
|
|
|
else
|
|
|
|
zpc.zpc_op = ZFS_PROJECT_OP_LIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (zpc.zpc_op) {
|
|
|
|
case ZFS_PROJECT_OP_LIST:
|
|
|
|
if (zpc.zpc_keep_projid) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-k' is only valid together with '-C'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (!zpc.zpc_newline) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-0' is only valid together with '-c'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ZFS_PROJECT_OP_CHECK:
|
|
|
|
if (zpc.zpc_keep_projid) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-k' is only valid together with '-C'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ZFS_PROJECT_OP_CLEAR:
|
|
|
|
if (zpc.zpc_dironly) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-d' is useless together with '-C'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (!zpc.zpc_newline) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-0' is only valid together with '-c'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-p' is useless together with '-C'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ZFS_PROJECT_OP_SET:
|
|
|
|
if (zpc.zpc_dironly) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-d' is useless for set project ID and/or "
|
|
|
|
"inherit flag\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (zpc.zpc_keep_projid) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-k' is only valid together with '-C'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (!zpc.zpc_newline) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("'-0' is only valid together with '-c'\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
argv += optind;
|
|
|
|
argc -= optind;
|
|
|
|
if (argc == 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("missing file or directory target(s)\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = zfs_project_handle(argv[i], &zpc);
|
|
|
|
if (err && !ret)
|
|
|
|
ret = err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2020-04-01 20:02:06 +03:00
|
|
|
static int
|
|
|
|
zfs_do_wait(int argc, char **argv)
|
|
|
|
{
|
|
|
|
boolean_t enabled[ZFS_WAIT_NUM_ACTIVITIES];
|
|
|
|
int error, i;
|
|
|
|
char c;
|
|
|
|
|
|
|
|
/* By default, wait for all types of activity. */
|
|
|
|
for (i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++)
|
|
|
|
enabled[i] = B_TRUE;
|
|
|
|
|
|
|
|
while ((c = getopt(argc, argv, "t:")) != -1) {
|
|
|
|
switch (c) {
|
|
|
|
case 't':
|
|
|
|
{
|
|
|
|
static char *col_subopts[] = { "deleteq", NULL };
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
/* Reset activities array */
|
|
|
|
bzero(&enabled, sizeof (enabled));
|
|
|
|
while (*optarg != '\0') {
|
|
|
|
int activity = getsubopt(&optarg, col_subopts,
|
|
|
|
&value);
|
|
|
|
|
|
|
|
if (activity < 0) {
|
|
|
|
(void) fprintf(stderr,
|
|
|
|
gettext("invalid activity '%s'\n"),
|
|
|
|
value);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
enabled[activity] = B_TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '?':
|
|
|
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
|
|
|
optopt);
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argv += optind;
|
|
|
|
argc -= optind;
|
|
|
|
if (argc < 1) {
|
|
|
|
(void) fprintf(stderr, gettext("missing 'filesystem' "
|
|
|
|
"argument\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 1) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
zfs_handle_t *zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
boolean_t missing = B_FALSE;
|
|
|
|
boolean_t any_waited = B_FALSE;
|
|
|
|
|
|
|
|
for (int i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++) {
|
|
|
|
boolean_t waited;
|
|
|
|
|
|
|
|
if (!enabled[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
error = zfs_wait_status(zhp, i, &missing, &waited);
|
|
|
|
if (error != 0 || missing)
|
|
|
|
break;
|
|
|
|
|
|
|
|
any_waited = (any_waited || waited);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error != 0 || missing || !any_waited)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2019-04-10 10:43:28 +03:00
|
|
|
/*
|
|
|
|
* Display version message
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_do_version(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if (zfs_version_print() == -1)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2012-04-24 14:51:02 +04:00
|
|
|
int ret = 0;
|
2010-08-26 20:58:04 +04:00
|
|
|
int i = 0;
|
2008-11-20 23:01:55 +03:00
|
|
|
char *cmdname;
|
2018-08-20 19:55:18 +03:00
|
|
|
char **newargv;
|
2008-11-20 23:01:55 +03:00
|
|
|
|
|
|
|
(void) setlocale(LC_ALL, "");
|
|
|
|
(void) textdomain(TEXT_DOMAIN);
|
|
|
|
|
|
|
|
opterr = 0;
|
|
|
|
|
|
|
|
/*
|
2011-03-05 02:14:46 +03:00
|
|
|
* Make sure the user has specified some command.
|
2008-11-20 23:01:55 +03:00
|
|
|
*/
|
2011-03-05 02:14:46 +03:00
|
|
|
if (argc < 2) {
|
|
|
|
(void) fprintf(stderr, gettext("missing command\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-03-05 02:14:46 +03:00
|
|
|
cmdname = argv[1];
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-03-05 02:14:46 +03:00
|
|
|
/*
|
|
|
|
* The 'umount' command is an alias for 'unmount'
|
|
|
|
*/
|
|
|
|
if (strcmp(cmdname, "umount") == 0)
|
|
|
|
cmdname = "unmount";
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2011-03-05 02:14:46 +03:00
|
|
|
/*
|
|
|
|
* The 'recv' command is an alias for 'receive'
|
|
|
|
*/
|
|
|
|
if (strcmp(cmdname, "recv") == 0)
|
|
|
|
cmdname = "receive";
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2012-04-05 17:03:21 +04:00
|
|
|
/*
|
|
|
|
* The 'snap' command is an alias for 'snapshot'
|
|
|
|
*/
|
|
|
|
if (strcmp(cmdname, "snap") == 0)
|
|
|
|
cmdname = "snapshot";
|
|
|
|
|
2011-03-05 02:14:46 +03:00
|
|
|
/*
|
|
|
|
* Special case '-?'
|
|
|
|
*/
|
|
|
|
if ((strcmp(cmdname, "-?") == 0) ||
|
|
|
|
(strcmp(cmdname, "--help") == 0))
|
|
|
|
usage(B_TRUE);
|
2008-11-20 23:01:55 +03:00
|
|
|
|
2019-04-10 10:43:28 +03:00
|
|
|
/*
|
|
|
|
* Special case '-V|--version'
|
|
|
|
*/
|
|
|
|
if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0))
|
|
|
|
return (zfs_do_version(argc, argv));
|
|
|
|
|
2015-05-21 00:39:52 +03:00
|
|
|
if ((g_zfs = libzfs_init()) == NULL) {
|
2019-09-18 19:05:57 +03:00
|
|
|
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
|
2011-03-05 02:14:46 +03:00
|
|
|
return (1);
|
2015-05-21 00:39:52 +03:00
|
|
|
}
|
2010-08-26 22:57:29 +04:00
|
|
|
|
2013-06-04 05:54:44 +04:00
|
|
|
mnttab_file = g_zfs->libzfs_mnttab;
|
|
|
|
|
2013-08-28 15:45:09 +04:00
|
|
|
zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
|
2010-08-26 22:57:29 +04:00
|
|
|
|
2011-03-05 02:14:46 +03:00
|
|
|
libzfs_print_on_error(g_zfs, B_TRUE);
|
2010-08-26 22:57:29 +04:00
|
|
|
|
2018-08-20 19:55:18 +03:00
|
|
|
/*
|
|
|
|
* Many commands modify input strings for string parsing reasons.
|
|
|
|
* We create a copy to protect the original argv.
|
|
|
|
*/
|
|
|
|
newargv = malloc((argc + 1) * sizeof (newargv[0]));
|
|
|
|
for (i = 0; i < argc; i++)
|
|
|
|
newargv[i] = strdup(argv[i]);
|
|
|
|
newargv[argc] = NULL;
|
|
|
|
|
2011-03-05 02:14:46 +03:00
|
|
|
/*
|
|
|
|
* Run the appropriate command.
|
|
|
|
*/
|
2014-01-07 21:21:20 +04:00
|
|
|
libzfs_mnttab_cache(g_zfs, B_TRUE);
|
2011-03-05 02:14:46 +03:00
|
|
|
if (find_command_idx(cmdname, &i) == 0) {
|
|
|
|
current_command = &command_table[i];
|
2018-08-20 19:55:18 +03:00
|
|
|
ret = command_table[i].func(argc - 1, newargv + 1);
|
2011-03-05 02:14:46 +03:00
|
|
|
} else if (strchr(cmdname, '=') != NULL) {
|
|
|
|
verify(find_command_idx("set", &i) == 0);
|
|
|
|
current_command = &command_table[i];
|
2018-08-20 19:55:18 +03:00
|
|
|
ret = command_table[i].func(argc, newargv);
|
2011-03-05 02:14:46 +03:00
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr, gettext("unrecognized "
|
|
|
|
"command '%s'\n"), cmdname);
|
|
|
|
usage(B_FALSE);
|
|
|
|
ret = 1;
|
2008-11-20 23:01:55 +03:00
|
|
|
}
|
|
|
|
|
2018-08-20 19:55:18 +03:00
|
|
|
for (i = 0; i < argc; i++)
|
|
|
|
free(newargv[i]);
|
|
|
|
free(newargv);
|
|
|
|
|
2013-08-28 15:45:09 +04:00
|
|
|
if (ret == 0 && log_history)
|
|
|
|
(void) zpool_log_history(g_zfs, history_str);
|
|
|
|
|
2013-12-24 00:06:34 +04:00
|
|
|
libzfs_fini(g_zfs);
|
|
|
|
|
2008-11-20 23:01:55 +03:00
|
|
|
/*
|
|
|
|
* The 'ZFS_ABORT' environment variable causes us to dump core on exit
|
|
|
|
* for the purposes of running ::findleaks.
|
|
|
|
*/
|
|
|
|
if (getenv("ZFS_ABORT") != NULL) {
|
|
|
|
(void) printf("dumping core by request\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
2019-12-11 22:58:37 +03:00
|
|
|
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
#include <sys/jail.h>
|
|
|
|
#include <jail.h>
|
|
|
|
/*
|
|
|
|
* Attach/detach the given dataset to/from the given jail
|
|
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
zfs_do_jail_impl(int argc, char **argv, boolean_t attach)
|
|
|
|
{
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
int jailid, ret;
|
|
|
|
|
|
|
|
/* check number of arguments */
|
|
|
|
if (argc < 3) {
|
|
|
|
(void) fprintf(stderr, gettext("missing argument(s)\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
if (argc > 3) {
|
|
|
|
(void) fprintf(stderr, gettext("too many arguments\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
jailid = jail_getid(argv[1]);
|
|
|
|
if (jailid < 0) {
|
|
|
|
(void) fprintf(stderr, gettext("invalid jail id or name\n"));
|
|
|
|
usage(B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
ret = (zfs_jail(zhp, jailid, attach) != 0);
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* zfs jail jailid filesystem
|
|
|
|
*
|
|
|
|
* Attach the given dataset to the given jail
|
|
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
zfs_do_jail(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (zfs_do_jail_impl(argc, argv, B_TRUE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* zfs unjail jailid filesystem
|
|
|
|
*
|
|
|
|
* Detach the given dataset from the given jail
|
|
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
zfs_do_unjail(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return (zfs_do_jail_impl(argc, argv, B_FALSE));
|
|
|
|
}
|
|
|
|
#endif
|