summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
authorJohn Sonnenschein <johns@joyent.com>2011-12-13 06:55:43 +0000
committerJohn Sonnenschein <johns@joyent.com>2011-12-13 06:55:43 +0000
commit73c531dd5303a633bcaaf609c6eeb2554096b478 (patch)
tree41cd342a9302e3f92a961716a5b27ba78912e937 /usr/src/lib
parentcf1f80d4478d278b94ad9c99026c1b589864f7da (diff)
parentf113db6b62228353583f701aefa9a8f2acbf64b8 (diff)
downloadillumos-joyent-73c531dd5303a633bcaaf609c6eeb2554096b478.tar.gz
Merge branch 'master' into gcc4
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/brand/joyent/zone/statechange.ksh36
-rw-r--r--usr/src/lib/brand/kvm/zone/config.xml1
-rwxr-xr-xusr/src/lib/brand/kvm/zone/kinstall.ksh5
-rwxr-xr-xusr/src/lib/brand/kvm/zone/statechange.ksh6
-rw-r--r--usr/src/lib/libbe/common/be_create.c42
-rw-r--r--usr/src/lib/libdtrace/Makefile.com3
-rw-r--r--usr/src/lib/libdtrace/common/dt_cc.c49
-rw-r--r--usr/src/lib/libdtrace/common/dt_consume.c30
-rw-r--r--usr/src/lib/libdtrace/common/dt_dof.c14
-rw-r--r--usr/src/lib/libdtrace/common/dt_errtags.h3
-rw-r--r--usr/src/lib/libdtrace/common/dt_impl.h7
-rw-r--r--usr/src/lib/libdtrace/common/dt_map.c172
-rw-r--r--usr/src/lib/libdtrace/common/dt_open.c10
-rw-r--r--usr/src/lib/libdtrace/common/dt_print.c642
-rw-r--r--usr/src/lib/libdtrace/common/dt_program.c2
-rw-r--r--usr/src/lib/libdtrace/common/dtrace.h13
-rw-r--r--usr/src/lib/libnsl/Makefile.com5
-rw-r--r--usr/src/lib/libnsl/common/llib-lnsl5
-rw-r--r--usr/src/lib/libnsl/common/mapfile-vers3
-rw-r--r--usr/src/lib/libnsl/nss/inet_matchaddr.c150
-rw-r--r--usr/src/lib/libsmbfs/netsmb/smbfs_api.h2
-rw-r--r--usr/src/lib/libsmbfs/smb/ntlmssp.c6
-rw-r--r--usr/src/lib/libxnet/common/mapfile-vers7
-rw-r--r--usr/src/lib/libzfs/Makefile.com3
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h56
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c454
-rw-r--r--usr/src/lib/libzfs/common/libzfs_graph.c653
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h4
-rw-r--r--usr/src/lib/libzfs/common/libzfs_import.c18
-rw-r--r--usr/src/lib/libzfs/common/libzfs_iter.c462
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c48
-rw-r--r--usr/src/lib/libzfs/common/libzfs_sendrecv.c547
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c5
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers11
-rw-r--r--usr/src/lib/libzonecfg/common/getzoneent.c8
-rw-r--r--usr/src/lib/libzonecfg/common/libzonecfg.c10
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/smb_share.c11
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c20
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_sam.c117
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_util.c249
40 files changed, 2525 insertions, 1364 deletions
diff --git a/usr/src/lib/brand/joyent/zone/statechange.ksh b/usr/src/lib/brand/joyent/zone/statechange.ksh
index 30e3e4be13..7d7d0213a4 100644
--- a/usr/src/lib/brand/joyent/zone/statechange.ksh
+++ b/usr/src/lib/brand/joyent/zone/statechange.ksh
@@ -63,13 +63,42 @@ SNAPSHOT_DIR=root/checkpoints
#
lock_file()
{
+ local cnt=0
+ local prev_pid=0
while true; do
if (set -o noclobber; echo "$$" >$LOCKFILE) 2>/dev/null; then
trap 'rm -f $LOCKFILE; exit $?' INT TERM EXIT
break;
- else
+ fi
+
+ local hold_pid=`cat $LOCKFILE 2>/dev/null`
+
+ # the file might be gone or empty when we run the cat cmd
+ if [[ -z "$hold_pid" ]]; then
sleep 1
+ cnt=0
+ continue
+ fi
+
+ # if held by a different process, restart counter
+ if [[ $prev_pid != $hold_pid ]]; then
+ prev_pid=$hold_pid
+ cnt=0
fi
+
+ [[ $cnt == 20 || $cnt == 40 ]] && \
+ logger -p daemon.err "dlmgmtd lock file $LOCKFILE" \
+ "held by pid $hold_pid for $cnt seconds"
+
+ if [[ $cnt == 60 ]]; then
+ logger -p daemon.err "breaking dlmgmtd lock" \
+ "held by pid $hold_pid after $cnt seconds"
+ unlock_file
+ continue
+ fi
+
+ sleep 1
+ let cnt=$cnt+1
done
}
@@ -197,10 +226,7 @@ setup_net()
# Set up antispoof options
- # XXX For backwards compatibility, special handling for
- # zone named "dhcpd". Remove this check once property
- # is added to zone.
- if [[ $ZONENAME == "dhcpd" ]] || [[ $dhcp_server == "1" ]]; then
+ if [[ $dhcp_server == "1" ]] || [[ $dhcp_server == "true" ]]; then
enable_dhcp="true"
# This needs to be off for dhcp server zones
allow_ip_spoof=1
diff --git a/usr/src/lib/brand/kvm/zone/config.xml b/usr/src/lib/brand/kvm/zone/config.xml
index af9b565be2..b49f839d67 100644
--- a/usr/src/lib/brand/kvm/zone/config.xml
+++ b/usr/src/lib/brand/kvm/zone/config.xml
@@ -74,6 +74,7 @@
<privilege set="default" name="net_privaddr" />
<privilege set="default" name="net_rawaccess" ip-type="exclusive" />
<privilege set="default" name="proc_chroot" />
+ <privilege set="default" name="proc_clock_highres" />
<privilege set="default" name="sys_audit" />
<privilege set="default" name="proc_audit" />
<privilege set="default" name="proc_lock_memory" />
diff --git a/usr/src/lib/brand/kvm/zone/kinstall.ksh b/usr/src/lib/brand/kvm/zone/kinstall.ksh
index 912fbeaeb1..abd60a17b7 100755
--- a/usr/src/lib/brand/kvm/zone/kinstall.ksh
+++ b/usr/src/lib/brand/kvm/zone/kinstall.ksh
@@ -81,13 +81,16 @@ if [ ! -d ${ZROOT}/tmp ]; then
chmod 1777 ${ZROOT}/tmp
fi
-
# The dataset quota must be a number.
case $ZQUOTA in *[!0-9]*)
print -u2 "Brand error: The quota $ZQUOTA is not a number"
exit $ZONE_SUBPROC_USAGE;;
esac
+if [[ ${ZQUOTA} != "0" ]]; then
+ zfs set quota=${ZQUOTA}g ${PDS_NAME}/${bname}
+fi
+
final_setup
exit $ZONE_SUBPROC_OK
diff --git a/usr/src/lib/brand/kvm/zone/statechange.ksh b/usr/src/lib/brand/kvm/zone/statechange.ksh
index d34177da62..6efe6f3cb5 100755
--- a/usr/src/lib/brand/kvm/zone/statechange.ksh
+++ b/usr/src/lib/brand/kvm/zone/statechange.ksh
@@ -91,6 +91,7 @@ setup_net()
do
# Get simplified versions of the network config. variables.
address=$(eval echo \$_ZONECFG_net_${nic}_address)
+ dhcp_server=$(eval echo \$_ZONECFG_net_${nic}_dhcp_server)
global_nic=$(eval echo \$_ZONECFG_net_${nic}_global_nic)
mac_addr=$(eval echo \$_ZONECFG_net_${nic}_mac_addr)
vlan_id=$(eval echo \$_ZONECFG_net_${nic}_vlan_id)
@@ -198,10 +199,7 @@ setup_net()
# Set up antispoof options
- # XXX For backwards compatibility, special handling for
- # zone named "dhcpd". Remove this check once property
- # is added to zone.
- if [[ $ZONENAME == "dhcpd" ]] || [[ $dhcp_server == "1" ]]; then
+ if [[ $dhcp_server == "1" ]] || [[ $dhcp_server == "true" ]]; then
enable_dhcp="true"
# This needs to be off for dhcp server zones
allow_ip_spoof=1
diff --git a/usr/src/lib/libbe/common/be_create.c b/usr/src/lib/libbe/common/be_create.c
index 0153be3c40..48bf4ec6bf 100644
--- a/usr/src/lib/libbe/common/be_create.c
+++ b/usr/src/lib/libbe/common/be_create.c
@@ -65,6 +65,7 @@ static int be_destroy_callback(zfs_handle_t *, void *);
static int be_send_fs_callback(zfs_handle_t *, void *);
static int be_demote_callback(zfs_handle_t *, void *);
static int be_demote_find_clone_callback(zfs_handle_t *, void *);
+static int be_has_snapshot_callback(zfs_handle_t *, void *);
static int be_demote_get_one_clone(zfs_handle_t *, void *);
static int be_get_snap(char *, char **);
static int be_prep_clone_send_fs(zfs_handle_t *, be_transaction_data_t *,
@@ -386,6 +387,7 @@ be_destroy(nvlist_t *be_attrs)
be_destroy_data_t dd = { 0 };
int ret = BE_SUCCESS;
uint16_t flags = 0;
+ boolean_t bs_found = B_FALSE;
int zret;
char obe_root_ds[MAXPATHLEN];
char *mp = NULL;
@@ -469,6 +471,16 @@ be_destroy(nvlist_t *be_attrs)
return (zfs_err_to_be_err(g_zfs));
}
+ /*
+ * Check if BE has snapshots and BE_DESTROY_FLAG_SNAPSHOTS
+ * is not set.
+ */
+ (void) zfs_iter_snapshots(zhp, be_has_snapshot_callback, &bs_found);
+ if (!dd.destroy_snaps && bs_found) {
+ ZFS_CLOSE(zhp);
+ return (BE_ERR_SS_EXISTS);
+ }
+
/* Get the UUID of the global BE */
if (be_get_uuid(zfs_get_name(zhp), &dd.gz_be_uuid) != BE_SUCCESS) {
be_print_err(gettext("be_destroy: BE has no UUID (%s)\n"),
@@ -1232,6 +1244,32 @@ be_exists_callback(zpool_handle_t *zlp, void *data)
}
/*
+ * Function: be_has_snapshots_callback
+ * Description: Callback function used to find out if a BE has snapshots.
+ * Parameters:
+ * zlp - zpool_handle_t pointer to the current pool being
+ * looked at.
+ * data - be_snap_found_t pointer.
+ * Return:
+ * 1 - BE has no snapshots.
+ * 0 - BE has snapshots.
+ * Scope:
+ * Private
+ */
+static int
+be_has_snapshot_callback(zfs_handle_t *zhp, void *data)
+{
+ boolean_t *bs = data;
+ if (zfs_get_name(zhp) == NULL) {
+ zfs_close(zhp);
+ return (1);
+ }
+ *bs = B_TRUE;
+ zfs_close(zhp);
+ return (0);
+}
+
+/*
* Function: be_set_uuid
* Description: This function generates a uuid, unparses it into
* string representation, and sets that string into
@@ -2387,7 +2425,7 @@ be_send_fs_callback(zfs_handle_t *zhp, void *data)
(void) close(srpipe[0]);
/* Send dataset */
- if (zfs_send(zhp, NULL, bt->obe_snap_name, send_flags,
+ if (zfs_send(zhp, NULL, bt->obe_snap_name, &send_flags,
srpipe[1], NULL, NULL, NULL) != 0) {
_exit(1);
}
@@ -2399,7 +2437,7 @@ be_send_fs_callback(zfs_handle_t *zhp, void *data)
(void) close(srpipe[1]);
/* Receive dataset */
- if (zfs_receive(g_zfs, clone_ds, flags, srpipe[0], NULL) != 0) {
+ if (zfs_receive(g_zfs, clone_ds, &flags, srpipe[0], NULL) != 0) {
be_print_err(gettext("be_send_fs_callback: failed to "
"recv dataset (%s)\n"), clone_ds);
}
diff --git a/usr/src/lib/libdtrace/Makefile.com b/usr/src/lib/libdtrace/Makefile.com
index 5d2d85caa5..095659d9ea 100644
--- a/usr/src/lib/libdtrace/Makefile.com
+++ b/usr/src/lib/libdtrace/Makefile.com
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
-#
+# Copyright (c) 2011 by Delphix. All rights reserved.
#
LIBRARY = libdtrace.a
@@ -53,6 +53,7 @@ LIBSRCS = \
dt_pcb.c \
dt_pid.c \
dt_pragma.c \
+ dt_print.c \
dt_printf.c \
dt_proc.c \
dt_provider.c \
diff --git a/usr/src/lib/libdtrace/common/dt_cc.c b/usr/src/lib/libdtrace/common/dt_cc.c
index c72cc93a8f..8b98b6d6f8 100644
--- a/usr/src/lib/libdtrace/common/dt_cc.c
+++ b/usr/src/lib/libdtrace/common/dt_cc.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, Joyent Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
@@ -678,6 +679,51 @@ dt_action_trace(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp)
ap->dtad_kind = DTRACEACT_DIFEXPR;
}
+/*
+ * The print() action behaves identically to trace(), except that it stores the
+ * CTF type of the argument (if present) within the DOF for the DIFEXPR action.
+ * To do this, we set the 'dtsd_strdata' to point to the fully-qualified CTF
+ * type ID for the result of the DIF action. We use the ID instead of the name
+ * to handles complex types like arrays and function pointers that can't be
+ * resolved by ctf_type_lookup(). This is later processed by
+ * dtrace_dof_create() and turned into a reference into the string table so
+ * that we can get the type information when we process the data after the
+ * fact.
+ */
+static void
+dt_action_print(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp)
+{
+ dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp);
+ dt_node_t *dret;
+ size_t len;
+ dt_module_t *dmp;
+
+ if (dt_node_is_void(dnp->dn_args)) {
+ dnerror(dnp->dn_args, D_PRINT_VOID,
+ "print( ) may not be applied to a void expression\n");
+ }
+
+ if (dt_node_is_dynamic(dnp->dn_args)) {
+ dnerror(dnp->dn_args, D_PRINT_DYN,
+ "print( ) may not be applied to a dynamic expression\n");
+ }
+
+ dt_cg(yypcb, dnp->dn_args);
+
+ dret = yypcb->pcb_dret;
+ dmp = dt_module_lookup_by_ctf(dtp, dret->dn_ctfp);
+
+ len = snprintf(NULL, 0, "%s`%d", dmp->dm_name, dret->dn_type) + 1;
+ sdp->dtsd_strdata = dt_alloc(dtp, len);
+ if (sdp->dtsd_strdata == NULL)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
+ (void) snprintf(sdp->dtsd_strdata, len, "%s`%d", dmp->dm_name,
+ dret->dn_type);
+
+ ap->dtad_difo = dt_as(yypcb);
+ ap->dtad_kind = DTRACEACT_DIFEXPR;
+}
+
static void
dt_action_tracemem(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp)
{
@@ -1057,6 +1103,9 @@ dt_compile_fun(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp)
case DT_ACT_TRACE:
dt_action_trace(dtp, dnp->dn_expr, sdp);
break;
+ case DT_ACT_PRINT:
+ dt_action_print(dtp, dnp->dn_expr, sdp);
+ break;
case DT_ACT_TRACEMEM:
dt_action_tracemem(dtp, dnp->dn_expr, sdp);
break;
diff --git a/usr/src/lib/libdtrace/common/dt_consume.c b/usr/src/lib/libdtrace/common/dt_consume.c
index 0d99a47e70..d3a554c1c6 100644
--- a/usr/src/lib/libdtrace/common/dt_consume.c
+++ b/usr/src/lib/libdtrace/common/dt_consume.c
@@ -25,6 +25,7 @@
/*
* Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <stdlib.h>
@@ -1975,6 +1976,35 @@ again:
goto nextrec;
}
+ /*
+ * If this is a DIF expression, and the record has a
+ * format set, this indicates we have a CTF type name
+ * associated with the data and we should try to print
+ * it out by type.
+ */
+ if (act == DTRACEACT_DIFEXPR) {
+ const char *strdata = dt_strdata_lookup(dtp,
+ rec->dtrd_format);
+ if (strdata != NULL) {
+ n = dtrace_print(dtp, fp, strdata,
+ addr, rec->dtrd_size);
+
+ /*
+ * dtrace_print() will return -1 on
+ * error, or return the number of bytes
+ * consumed. It will return 0 if the
+ * type couldn't be determined, and we
+ * should fall through to the normal
+ * trace method.
+ */
+ if (n < 0)
+ return (-1);
+
+ if (n > 0)
+ goto nextrec;
+ }
+ }
+
nofmt:
if (act == DTRACEACT_PRINTA) {
dt_print_aggdata_t pd;
diff --git a/usr/src/lib/libdtrace/common/dt_dof.c b/usr/src/lib/libdtrace/common/dt_dof.c
index a7eb8e4d23..04c4c89cdb 100644
--- a/usr/src/lib/libdtrace/common/dt_dof.c
+++ b/usr/src/lib/libdtrace/common/dt_dof.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <sys/types.h>
@@ -754,16 +755,23 @@ dtrace_dof_create(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t flags)
dofa[i].dofa_difo = DOF_SECIDX_NONE;
/*
- * If the first action in a statement has format data,
- * add the format string to the global string table.
+ * If the first action in a statement has string data,
+ * add the string to the global string table. This can
+ * be due either to a printf() format string
+ * (dtsd_fmtdata) or a print() type string
+ * (dtsd_strdata).
*/
if (sdp != NULL && ap == sdp->dtsd_action) {
if (sdp->dtsd_fmtdata != NULL) {
(void) dtrace_printf_format(dtp,
sdp->dtsd_fmtdata, fmt, maxfmt + 1);
strndx = dof_add_string(ddo, fmt);
- } else
+ } else if (sdp->dtsd_strdata != NULL) {
+ strndx = dof_add_string(ddo,
+ sdp->dtsd_strdata);
+ } else {
strndx = 0; /* use dtad_arg instead */
+ }
if ((next = dt_list_next(next)) != NULL)
sdp = next->ds_desc;
diff --git a/usr/src/lib/libdtrace/common/dt_errtags.h b/usr/src/lib/libdtrace/common/dt_errtags.h
index 106ee1847e..473e2addc7 100644
--- a/usr/src/lib/libdtrace/common/dt_errtags.h
+++ b/usr/src/lib/libdtrace/common/dt_errtags.h
@@ -26,6 +26,7 @@
/*
* Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _DT_ERRTAGS_H
@@ -189,6 +190,8 @@ typedef enum {
D_PRINTA_AGGPROTO, /* printa() aggregation mismatch */
D_TRACE_VOID, /* trace() argument has void type */
D_TRACE_DYN, /* trace() argument has dynamic type */
+ D_PRINT_VOID, /* print() argument has void type */
+ D_PRINT_DYN, /* print() argument has dynamic type */
D_TRACEMEM_ADDR, /* tracemem() address bad type */
D_TRACEMEM_SIZE, /* tracemem() size bad type */
D_TRACEMEM_ARGS, /* tracemem() illegal number of args */
diff --git a/usr/src/lib/libdtrace/common/dt_impl.h b/usr/src/lib/libdtrace/common/dt_impl.h
index 90bb70d866..7b9eb5e78b 100644
--- a/usr/src/lib/libdtrace/common/dt_impl.h
+++ b/usr/src/lib/libdtrace/common/dt_impl.h
@@ -26,6 +26,7 @@
/*
* Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _DT_IMPL_H
@@ -240,6 +241,8 @@ struct dtrace_hdl {
dtrace_aggdesc_t **dt_aggdesc; /* aggregation descriptions */
int dt_maxformat; /* max format ID */
void **dt_formats; /* pointer to format array */
+ int dt_maxstrdata; /* max strdata ID */
+ char **dt_strdata; /* pointer to strdata array */
dt_aggregate_t dt_aggregate; /* aggregate */
dtrace_bufdesc_t dt_buf; /* staging buffer */
struct dt_pfdict *dt_pfdict; /* dictionary of printf conversions */
@@ -418,6 +421,7 @@ struct dtrace_hdl {
#define DT_ACT_UMOD DT_ACT(26) /* umod() action */
#define DT_ACT_UADDR DT_ACT(27) /* uaddr() action */
#define DT_ACT_SETOPT DT_ACT(28) /* setopt() action */
+#define DT_ACT_PRINT DT_ACT(29) /* print() action */
/*
* Sentinel to tell freopen() to restore the saved stdout. This must not
@@ -601,6 +605,9 @@ extern void dt_aggid_destroy(dtrace_hdl_t *);
extern void *dt_format_lookup(dtrace_hdl_t *, int);
extern void dt_format_destroy(dtrace_hdl_t *);
+extern const char *dt_strdata_lookup(dtrace_hdl_t *, int);
+extern void dt_strdata_destroy(dtrace_hdl_t *);
+
extern int dt_print_quantize(dtrace_hdl_t *, FILE *,
const void *, size_t, uint64_t);
extern int dt_print_lquantize(dtrace_hdl_t *, FILE *,
diff --git a/usr/src/lib/libdtrace/common/dt_map.c b/usr/src/lib/libdtrace/common/dt_map.c
index 15361862de..8a3ce817e4 100644
--- a/usr/src/lib/libdtrace/common/dt_map.c
+++ b/usr/src/lib/libdtrace/common/dt_map.c
@@ -23,7 +23,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
#include <stdlib.h>
#include <strings.h>
@@ -35,10 +37,81 @@
#include <dt_printf.h>
static int
+dt_strdata_add(dtrace_hdl_t *dtp, dtrace_recdesc_t *rec, void ***data, int *max)
+{
+ int maxformat;
+ dtrace_fmtdesc_t fmt;
+ void *result;
+
+ if (rec->dtrd_format == 0)
+ return (0);
+
+ if (rec->dtrd_format <= *max &&
+ (*data)[rec->dtrd_format - 1] != NULL) {
+ return (0);
+ }
+
+ bzero(&fmt, sizeof (fmt));
+ fmt.dtfd_format = rec->dtrd_format;
+ fmt.dtfd_string = NULL;
+ fmt.dtfd_length = 0;
+
+ if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1)
+ return (dt_set_errno(dtp, errno));
+
+ if ((fmt.dtfd_string = dt_alloc(dtp, fmt.dtfd_length)) == NULL)
+ return (dt_set_errno(dtp, EDT_NOMEM));
+
+ if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) {
+ free(fmt.dtfd_string);
+ return (dt_set_errno(dtp, errno));
+ }
+
+ while (rec->dtrd_format > (maxformat = *max)) {
+ int new_max = maxformat ? (maxformat << 1) : 1;
+ size_t nsize = new_max * sizeof (void *);
+ size_t osize = maxformat * sizeof (void *);
+ void **new_data = dt_zalloc(dtp, nsize);
+
+ if (new_data == NULL) {
+ dt_free(dtp, fmt.dtfd_string);
+ return (dt_set_errno(dtp, EDT_NOMEM));
+ }
+
+ bcopy(*data, new_data, osize);
+ free(*data);
+
+ *data = new_data;
+ *max = new_max;
+ }
+
+ switch (rec->dtrd_action) {
+ case DTRACEACT_DIFEXPR:
+ result = fmt.dtfd_string;
+ break;
+ case DTRACEACT_PRINTA:
+ result = dtrace_printa_create(dtp, fmt.dtfd_string);
+ dt_free(dtp, fmt.dtfd_string);
+ break;
+ default:
+ result = dtrace_printf_create(dtp, fmt.dtfd_string);
+ dt_free(dtp, fmt.dtfd_string);
+ break;
+ }
+
+ if (result == NULL)
+ return (-1);
+
+ (*data)[rec->dtrd_format - 1] = result;
+
+ return (0);
+}
+
+static int
dt_epid_add(dtrace_hdl_t *dtp, dtrace_epid_t id)
{
dtrace_id_t max;
- int rval, i, maxformat;
+ int rval, i;
dtrace_eprobedesc_t *enabled, *nenabled;
dtrace_probedesc_t *probe;
@@ -124,71 +197,23 @@ dt_epid_add(dtrace_hdl_t *dtp, dtrace_epid_t id)
}
for (i = 0; i < enabled->dtepd_nrecs; i++) {
- dtrace_fmtdesc_t fmt;
dtrace_recdesc_t *rec = &enabled->dtepd_rec[i];
- if (!DTRACEACT_ISPRINTFLIKE(rec->dtrd_action))
- continue;
-
- if (rec->dtrd_format == 0)
- continue;
-
- if (rec->dtrd_format <= dtp->dt_maxformat &&
- dtp->dt_formats[rec->dtrd_format - 1] != NULL)
- continue;
-
- bzero(&fmt, sizeof (fmt));
- fmt.dtfd_format = rec->dtrd_format;
- fmt.dtfd_string = NULL;
- fmt.dtfd_length = 0;
-
- if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) {
- rval = dt_set_errno(dtp, errno);
- goto err;
- }
-
- if ((fmt.dtfd_string = malloc(fmt.dtfd_length)) == NULL) {
- rval = dt_set_errno(dtp, EDT_NOMEM);
- goto err;
- }
-
- if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) {
- rval = dt_set_errno(dtp, errno);
- free(fmt.dtfd_string);
- goto err;
- }
-
- while (rec->dtrd_format > (maxformat = dtp->dt_maxformat)) {
- int new_max = maxformat ? (maxformat << 1) : 1;
- size_t nsize = new_max * sizeof (void *);
- size_t osize = maxformat * sizeof (void *);
- void **new_formats = malloc(nsize);
-
- if (new_formats == NULL) {
- rval = dt_set_errno(dtp, EDT_NOMEM);
- free(fmt.dtfd_string);
+ if (DTRACEACT_ISPRINTFLIKE(rec->dtrd_action)) {
+ if (dt_strdata_add(dtp, rec, &dtp->dt_formats,
+ &dtp->dt_maxformat) != 0) {
+ rval = -1;
+ goto err;
+ }
+ } else if (rec->dtrd_action == DTRACEACT_DIFEXPR) {
+ if (dt_strdata_add(dtp, rec,
+ (void ***)&dtp->dt_strdata,
+ &dtp->dt_maxstrdata) != 0) {
+ rval = -1;
goto err;
}
-
- bzero(new_formats, nsize);
- bcopy(dtp->dt_formats, new_formats, osize);
- free(dtp->dt_formats);
-
- dtp->dt_formats = new_formats;
- dtp->dt_maxformat = new_max;
}
- dtp->dt_formats[rec->dtrd_format - 1] =
- rec->dtrd_action == DTRACEACT_PRINTA ?
- dtrace_printa_create(dtp, fmt.dtfd_string) :
- dtrace_printf_create(dtp, fmt.dtfd_string);
-
- free(fmt.dtfd_string);
-
- if (dtp->dt_formats[rec->dtrd_format - 1] == NULL) {
- rval = -1; /* dt_errno is set for us */
- goto err;
- }
}
dtp->dt_pdesc[id] = probe;
@@ -424,3 +449,28 @@ dt_aggid_destroy(dtrace_hdl_t *dtp)
dtp->dt_aggdesc = NULL;
dtp->dt_maxagg = 0;
}
+
+const char *
+dt_strdata_lookup(dtrace_hdl_t *dtp, int idx)
+{
+ if (idx == 0 || idx > dtp->dt_maxstrdata)
+ return (NULL);
+
+ if (dtp->dt_strdata == NULL)
+ return (NULL);
+
+ return (dtp->dt_strdata[idx - 1]);
+}
+
+void
+dt_strdata_destroy(dtrace_hdl_t *dtp)
+{
+ int i;
+
+ for (i = 0; i < dtp->dt_maxstrdata; i++) {
+ free(dtp->dt_strdata[i]);
+ }
+
+ free(dtp->dt_strdata);
+ dtp->dt_strdata = NULL;
+}
diff --git a/usr/src/lib/libdtrace/common/dt_open.c b/usr/src/lib/libdtrace/common/dt_open.c
index 211ff67276..502a9d42ad 100644
--- a/usr/src/lib/libdtrace/common/dt_open.c
+++ b/usr/src/lib/libdtrace/common/dt_open.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <sys/types.h>
@@ -108,8 +109,9 @@
#define DT_VERS_1_7_1 DT_VERSION_NUMBER(1, 7, 1)
#define DT_VERS_1_8 DT_VERSION_NUMBER(1, 8, 0)
#define DT_VERS_1_8_1 DT_VERSION_NUMBER(1, 8, 1)
-#define DT_VERS_LATEST DT_VERS_1_8_1
-#define DT_VERS_STRING "Sun D 1.8.1"
+#define DT_VERS_1_9 DT_VERSION_NUMBER(1, 9, 0)
+#define DT_VERS_LATEST DT_VERS_1_9
+#define DT_VERS_STRING "Sun D 1.9"
const dt_version_t _dtrace_versions[] = {
DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */
@@ -129,6 +131,7 @@ const dt_version_t _dtrace_versions[] = {
DT_VERS_1_7_1, /* D API 1.7.1 */
DT_VERS_1_8, /* D API 1.8 */
DT_VERS_1_8_1, /* D API 1.8.1 */
+ DT_VERS_1_9, /* D API 1.9 */
0
};
@@ -304,6 +307,8 @@ static const dt_ident_t _dtrace_globals[] = {
&dt_idops_type, "pid_t" },
{ "ppid", DT_IDENT_SCALAR, 0, DIF_VAR_PPID, DT_ATTR_STABCMN, DT_VERS_1_0,
&dt_idops_type, "pid_t" },
+{ "print", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINT, DT_ATTR_STABCMN, DT_VERS_1_9,
+ &dt_idops_func, "void(@)" },
{ "printa", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTA, DT_ATTR_STABCMN, DT_VERS_1_0,
&dt_idops_func, "void(@, ...)" },
{ "printf", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTF, DT_ATTR_STABCMN, DT_VERS_1_0,
@@ -1357,6 +1362,7 @@ dtrace_close(dtrace_hdl_t *dtp)
dt_epid_destroy(dtp);
dt_aggid_destroy(dtp);
dt_format_destroy(dtp);
+ dt_strdata_destroy(dtp);
dt_buffered_destroy(dtp);
dt_aggregate_destroy(dtp);
free(dtp->dt_buf.dtbd_data);
diff --git a/usr/src/lib/libdtrace/common/dt_print.c b/usr/src/lib/libdtrace/common/dt_print.c
new file mode 100644
index 0000000000..5841699ad0
--- /dev/null
+++ b/usr/src/lib/libdtrace/common/dt_print.c
@@ -0,0 +1,642 @@
+/*
+ * 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
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+/*
+ * DTrace print() action
+ *
+ * This file contains the post-processing logic for the print() action. The
+ * print action behaves identically to trace() in that it generates a
+ * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type
+ * string stored in the DOF string table (similar to printf formats). We
+ * take the result of the trace action and post-process it in the fashion of
+ * MDB's ::print dcmd.
+ *
+ * This implementation differs from MDB's in the following ways:
+ *
+ * - We do not expose any options or flags. The behavior of print() is
+ * equivalent to "::print -tn".
+ *
+ * - MDB will display "holes" in structures (unused padding between
+ * members).
+ *
+ * - When printing arrays of structures, MDB will leave a trailing ','
+ * after the last element.
+ *
+ * - MDB will print time_t types as date and time.
+ *
+ * - MDB will detect when an enum is actually the OR of several flags,
+ * and print it out with the constituent flags separated.
+ *
+ * - For large arrays, MDB will print the first few members and then
+ * print a "..." continuation line.
+ *
+ * - MDB will break and wrap arrays at 80 columns.
+ *
+ * - MDB prints out floats and doubles by hand, as it must run in kmdb
+ * context. We're able to leverage the printf() format strings,
+ * but the result is a slightly different format.
+ */
+
+#include <sys/sysmacros.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <dt_module.h>
+#include <dt_printf.h>
+#include <dt_string.h>
+#include <dt_impl.h>
+
+/* determines whether the given integer CTF encoding is a character */
+#define CTF_IS_CHAR(e) \
+ (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \
+ (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY)
+/* determines whether the given CTF kind is a struct or union */
+#define CTF_IS_STRUCTLIKE(k) \
+ ((k) == CTF_K_STRUCT || (k) == CTF_K_UNION)
+
+/*
+ * Print structure passed down recursively through printing algorithm.
+ */
+typedef struct dt_printarg {
+ caddr_t pa_addr; /* base address of trace data */
+ ctf_file_t *pa_ctfp; /* CTF container */
+ int pa_depth; /* member depth */
+ int pa_nest; /* nested array depth */
+ FILE *pa_file; /* output file */
+} dt_printarg_t;
+
+static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *);
+
+/*
+ * Safe version of ctf_type_name() that will fall back to just "<ctfid>" if it
+ * can't resolve the type.
+ */
+static void
+dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen)
+{
+ if (ctf_type_name(ctfp, id, buf, buflen) == NULL)
+ (void) snprintf(buf, buflen, "<%ld>", id);
+}
+
+/*
+ * Print any necessary trailing braces for structures or unions. We don't get
+ * invoked when a struct or union ends, so we infer the need to print braces
+ * based on the depth the last time we printed something and the new depth.
+ */
+static void
+dt_print_trailing_braces(dt_printarg_t *pap, int depth)
+{
+ int d;
+
+ for (d = pap->pa_depth; d > depth; d--) {
+ (void) fprintf(pap->pa_file, "%*s}%s",
+ (d + pap->pa_nest - 1) * 4, "",
+ d == depth + 1 ? "" : "\n");
+ }
+}
+
+/*
+ * Print the appropriate amount of indentation given the current depth and
+ * array nesting.
+ */
+static void
+dt_print_indent(dt_printarg_t *pap)
+{
+ (void) fprintf(pap->pa_file, "%*s",
+ (pap->pa_depth + pap->pa_nest) * 4, "");
+}
+
+/*
+ * Print a bitfield. It's worth noting that the D compiler support for
+ * bitfields is currently broken; printing "D`user_desc_t" (pulled in by the
+ * various D provider files) will produce incorrect results compared to
+ * "genunix`user_desc_t".
+ */
+static void
+print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep)
+{
+ FILE *fp = pap->pa_file;
+ caddr_t addr = pap->pa_addr + off / NBBY;
+ uint64_t mask = (1ULL << ep->cte_bits) - 1;
+ uint64_t value = 0;
+ size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY;
+ uint8_t *buf = (uint8_t *)&value;
+ uint8_t shift;
+
+ /*
+ * On big-endian machines, we need to adjust the buf pointer to refer
+ * to the lowest 'size' bytes in 'value', and we need to shift based on
+ * the offset from the end of the data, not the offset of the start.
+ */
+#ifdef _BIG_ENDIAN
+ buf += sizeof (value) - size;
+ off += ep->cte_bits;
+#endif
+ bcopy(addr, buf, size);
+ shift = off % NBBY;
+
+ /*
+ * Offsets are counted from opposite ends on little- and
+ * big-endian machines.
+ */
+#ifdef _BIG_ENDIAN
+ shift = NBBY - shift;
+#endif
+
+ /*
+ * If the bits we want do not begin on a byte boundary, shift the data
+ * right so that the value is in the lowest 'cte_bits' of 'value'.
+ */
+ if (off % NBBY != 0)
+ value >>= shift;
+ value &= mask;
+
+ (void) fprintf(fp, "%#llx", (u_longlong_t)value);
+}
+
+/*
+ * Dump the contents of memory as a fixed-size integer in hex.
+ */
+static void
+dt_print_hex(FILE *fp, caddr_t addr, size_t size)
+{
+ switch (size) {
+ case sizeof (uint8_t):
+ (void) fprintf(fp, "%#x", *(uint8_t *)addr);
+ break;
+ case sizeof (uint16_t):
+ (void) fprintf(fp, "%#x", *(uint16_t *)addr);
+ break;
+ case sizeof (uint32_t):
+ (void) fprintf(fp, "%#x", *(uint32_t *)addr);
+ break;
+ case sizeof (uint64_t):
+ (void) fprintf(fp, "%#llx",
+ (unsigned long long)*(uint64_t *)addr);
+ break;
+ default:
+ (void) fprintf(fp, "<invalid size %u>", (uint_t)size);
+ }
+}
+
+/*
+ * Print an integer type. Before dumping the contents via dt_print_hex(), we
+ * first check the encoding to see if it's part of a bitfield or a character.
+ */
+static void
+dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+ FILE *fp = pap->pa_file;
+ ctf_file_t *ctfp = pap->pa_ctfp;
+ ctf_encoding_t e;
+ size_t size;
+ caddr_t addr = pap->pa_addr + off / NBBY;
+
+ if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) {
+ (void) fprintf(fp, "<unknown encoding>");
+ return;
+ }
+
+ /*
+ * This comes from MDB - it's not clear under what circumstances this
+ * would be found.
+ */
+ if (e.cte_format & CTF_INT_VARARGS) {
+ (void) fprintf(fp, "...");
+ return;
+ }
+
+ /*
+ * We print this as a bitfield if the bit encoding indicates it's not
+ * an even power of two byte size, or is larger than 8 bytes.
+ */
+ size = e.cte_bits / NBBY;
+ if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) {
+ print_bitfield(pap, off, &e);
+ return;
+ }
+
+ /*
+ * If this is a character, print it out as such.
+ */
+ if (CTF_IS_CHAR(e)) {
+ char c = *(char *)addr;
+ if (isprint(c))
+ (void) fprintf(fp, "'%c'", c);
+ else if (c == 0)
+ (void) fprintf(fp, "'\\0'");
+ else
+ (void) fprintf(fp, "'\\%03o'", c);
+ return;
+ }
+
+ dt_print_hex(fp, addr, size);
+}
+
+/*
+ * Print a floating point (float, double, long double) value.
+ */
+/* ARGSUSED */
+static void
+dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+ FILE *fp = pap->pa_file;
+ ctf_file_t *ctfp = pap->pa_ctfp;
+ ctf_encoding_t e;
+ caddr_t addr = pap->pa_addr + off / NBBY;
+
+ if (ctf_type_encoding(ctfp, base, &e) == 0) {
+ if (e.cte_format == CTF_FP_SINGLE &&
+ e.cte_bits == sizeof (float) * NBBY) {
+ (void) fprintf(fp, "%+.7e", *((float *)addr));
+ } else if (e.cte_format == CTF_FP_DOUBLE &&
+ e.cte_bits == sizeof (double) * NBBY) {
+ (void) fprintf(fp, "%+.7e", *((double *)addr));
+ } else if (e.cte_format == CTF_FP_LDOUBLE &&
+ e.cte_bits == sizeof (long double) * NBBY) {
+ (void) fprintf(fp, "%+.16LE", *((long double *)addr));
+ } else {
+ (void) fprintf(fp, "<unknown encoding>");
+ }
+ }
+}
+
+/*
+ * A pointer is printed as a fixed-size integer. This is used both for
+ * pointers and functions.
+ */
+static void
+dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+ FILE *fp = pap->pa_file;
+ ctf_file_t *ctfp = pap->pa_ctfp;
+ caddr_t addr = pap->pa_addr + off / NBBY;
+ size_t size = ctf_type_size(ctfp, base);
+
+ dt_print_hex(fp, addr, size);
+}
+
+/*
+ * Print out an array. This is somewhat complex, as we must manually visit
+ * each member, and recursively invoke ctf_type_visit() for each member. If
+ * the members are non-structs, then we print them out directly:
+ *
+ * [ 0x14, 0x2e, 0 ]
+ *
+ * If they are structs, then we print out the necessary leading and trailing
+ * braces, to end up with:
+ *
+ * [
+ * type {
+ * ...
+ * },
+ * type {
+ * ...
+ * }
+ * ]
+ *
+ * We also use a heuristic to detect whether the array looks like a character
+ * array. If the encoding indicates it's a character, and we have all
+ * printable characters followed by a null byte, then we display it as a
+ * string:
+ *
+ * [ "string" ]
+ */
+static void
+dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+ FILE *fp = pap->pa_file;
+ ctf_file_t *ctfp = pap->pa_ctfp;
+ caddr_t addr = pap->pa_addr + off / NBBY;
+ ctf_arinfo_t car;
+ ssize_t eltsize;
+ ctf_encoding_t e;
+ int i;
+ boolean_t isstring;
+ int kind;
+ ctf_id_t rtype;
+
+ if (ctf_array_info(ctfp, base, &car) == CTF_ERR) {
+ (void) fprintf(fp, "0x%p", (void *)addr);
+ return;
+ }
+
+ if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 ||
+ (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR ||
+ (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) {
+ (void) fprintf(fp, "<invalid type %lu>", car.ctr_contents);
+ return;
+ }
+
+ /* see if this looks like a string */
+ isstring = B_FALSE;
+ if (kind == CTF_K_INTEGER &&
+ ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) {
+ char c;
+ for (i = 0; i < car.ctr_nelems; i++) {
+ c = *((char *)addr + eltsize * i);
+ if (!isprint(c) || c == '\0')
+ break;
+ }
+
+ if (i != car.ctr_nelems && c == '\0')
+ isstring = B_TRUE;
+ }
+
+ /*
+ * As a slight aesthetic optimization, if we are a top-level type, then
+ * don't bother printing out the brackets. This lets print("foo") look
+ * like:
+ *
+ * string "foo"
+ *
+ * As D will internally represent this as a char[256] array.
+ */
+ if (!isstring || pap->pa_depth != 0)
+ (void) fprintf(fp, "[ ");
+
+ if (isstring)
+ (void) fprintf(fp, "\"");
+
+ for (i = 0; i < car.ctr_nelems; i++) {
+ if (isstring) {
+ char c = *((char *)addr + eltsize * i);
+ if (c == '\0')
+ break;
+ (void) fprintf(fp, "%c", c);
+ } else {
+ /*
+ * Recursively invoke ctf_type_visit() on each member.
+ * We setup a new printarg struct with 'pa_nest' set to
+ * indicate that we are within a nested array.
+ */
+ dt_printarg_t pa = *pap;
+ pa.pa_nest += pap->pa_depth + 1;
+ pa.pa_depth = 0;
+ pa.pa_addr = addr + eltsize * i;
+ (void) ctf_type_visit(ctfp, car.ctr_contents,
+ dt_print_member, &pa);
+
+ dt_print_trailing_braces(&pa, 0);
+ if (i != car.ctr_nelems - 1)
+ (void) fprintf(fp, ", ");
+ else if (CTF_IS_STRUCTLIKE(kind))
+ (void) fprintf(fp, "\n");
+ }
+ }
+
+ if (isstring)
+ (void) fprintf(fp, "\"");
+
+ if (!isstring || pap->pa_depth != 0) {
+ if (CTF_IS_STRUCTLIKE(kind))
+ dt_print_indent(pap);
+ else
+ (void) fprintf(fp, " ");
+ (void) fprintf(fp, "]");
+ }
+}
+
+/*
+ * This isued by both structs and unions to print the leading brace.
+ */
+/* ARGSUSED */
+static void
+dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap)
+{
+ (void) fprintf(pap->pa_file, "{");
+}
+
+/*
+ * For enums, we try to print the enum name, and fall back to the value if it
+ * can't be determined. We do not do any fancy flag processing like mdb.
+ */
+/* ARGSUSED */
+static void
+dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+ FILE *fp = pap->pa_file;
+ ctf_file_t *ctfp = pap->pa_ctfp;
+ const char *ename;
+ int value = 0;
+
+ if ((ename = ctf_enum_name(ctfp, base, value)) != NULL)
+ (void) fprintf(fp, "%s", ename);
+ else
+ (void) fprintf(fp, "%d", value);
+}
+
+/*
+ * Forward declaration. There's not much to do here without the complete
+ * type information, so just print out this fact and drive on.
+ */
+/* ARGSUSED */
+static void
+dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+ (void) fprintf(pap->pa_file, "<forward decl>");
+}
+
+typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *);
+
+static dt_printarg_f *const dt_printfuncs[] = {
+ dt_print_int, /* CTF_K_INTEGER */
+ dt_print_float, /* CTF_K_FLOAT */
+ dt_print_ptr, /* CTF_K_POINTER */
+ dt_print_array, /* CTF_K_ARRAY */
+ dt_print_ptr, /* CTF_K_FUNCTION */
+ dt_print_structlike, /* CTF_K_STRUCT */
+ dt_print_structlike, /* CTF_K_UNION */
+ dt_print_enum, /* CTF_K_ENUM */
+ dt_print_tag /* CTF_K_FORWARD */
+};
+
+/*
+ * Print one member of a structure. This callback is invoked from
+ * ctf_type_visit() recursively.
+ */
+static int
+dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth,
+ void *data)
+{
+ char type[DT_TYPE_NAMELEN];
+ int kind;
+ dt_printarg_t *pap = data;
+ FILE *fp = pap->pa_file;
+ ctf_file_t *ctfp = pap->pa_ctfp;
+ boolean_t arraymember;
+ boolean_t brief;
+ ctf_encoding_t e;
+ ctf_id_t rtype;
+
+ dt_print_trailing_braces(pap, depth);
+ /*
+ * dt_print_trailing_braces() doesn't include the trailing newline; add
+ * it here if necessary.
+ */
+ if (depth < pap->pa_depth)
+ (void) fprintf(fp, "\n");
+ pap->pa_depth = depth;
+
+ if ((rtype = ctf_type_resolve(ctfp, id)) == CTF_ERR ||
+ (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR ||
+ kind < CTF_K_INTEGER || kind > CTF_K_FORWARD) {
+ dt_print_indent(pap);
+ (void) fprintf(fp, "%s = <invalid type %lu>", name, id);
+ return (0);
+ }
+
+ dt_print_type_name(ctfp, id, type, sizeof (type));
+
+ arraymember = (pap->pa_nest != 0 && depth == 0);
+ brief = (arraymember && !CTF_IS_STRUCTLIKE(kind));
+
+ if (!brief) {
+ /*
+ * If this is a direct array member and a struct (otherwise
+ * brief would be true), then print a trailing newline, as the
+ * array printing code doesn't include it because it might be a
+ * simple type.
+ */
+ if (arraymember)
+ (void) fprintf(fp, "\n");
+ dt_print_indent(pap);
+
+ /* always print the type */
+ (void) fprintf(fp, "%s", type);
+ if (name[0] != '\0') {
+ /*
+ * For aesthetics, we don't include a space between the
+ * type name and member name if the type is a pointer.
+ * This will give us "void *foo =" instead of "void *
+ * foo =". Unions also have the odd behavior that the
+ * type name is returned as "union ", with a trailing
+ * space, so we also avoid printing a space if the type
+ * name already ends with a space.
+ */
+ if (type[strlen(type) - 1] != '*' &&
+ type[strlen(type) -1] != ' ') {
+ (void) fprintf(fp, " ");
+ }
+ (void) fprintf(fp, "%s", name);
+
+ /*
+ * If this looks like a bitfield, or is an integer not
+ * aligned on a byte boundary, print the number of
+ * bits after the name.
+ */
+ if (kind == CTF_K_INTEGER &&
+ ctf_type_encoding(ctfp, id, &e) == 0) {
+ ulong_t bits = e.cte_bits;
+ ulong_t size = bits / NBBY;
+
+ if (bits % NBBY != 0 ||
+ off % NBBY != 0 ||
+ size > 8 ||
+ size != ctf_type_size(ctfp, id)) {
+ (void) fprintf(fp, " :%lu", bits);
+ }
+ }
+
+ (void) fprintf(fp, " =");
+ }
+ (void) fprintf(fp, " ");
+ }
+
+ dt_printfuncs[kind - 1](rtype, off, pap);
+
+ /* direct simple array members are not separated by newlines */
+ if (!brief)
+ (void) fprintf(fp, "\n");
+
+ return (0);
+}
+
+/*
+ * Main print function invoked by dt_consume_cpu().
+ */
+int
+dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename,
+ caddr_t addr, size_t len)
+{
+ const char *s;
+ char *object;
+ dt_printarg_t pa;
+ ctf_id_t id;
+ dt_module_t *dmp;
+
+ /*
+ * Split the fully-qualified type ID (module`id). This should
+ * always be the format, but if for some reason we don't find the
+ * expected value, return 0 to fall back to the generic trace()
+ * behavior.
+ */
+ for (s = typename; *s != '\0' && *s != '`'; s++)
+ ;
+
+ if (*s != '`')
+ return (0);
+
+ object = alloca(s - typename + 1);
+ bcopy(typename, object, s - typename);
+ object[s - typename] = '\0';
+ id = atoi(s + 1);
+
+ /*
+ * Try to get the CTF kind for this id. If something has gone horribly
+ * wrong and we can't resolve the ID, bail out and let trace() do the
+ * work.
+ */
+ dmp = dt_module_lookup_by_name(dtp, object);
+ if (dmp == NULL || ctf_type_kind(dt_module_getctf(dtp, dmp),
+ id) == CTF_ERR) {
+ return (0);
+ }
+
+ /* setup the print structure and kick off the main print routine */
+ pa.pa_addr = addr;
+ pa.pa_ctfp = dt_module_getctf(dtp, dmp);
+ pa.pa_nest = 0;
+ pa.pa_depth = 0;
+ pa.pa_file = fp;
+ (void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa);
+
+ dt_print_trailing_braces(&pa, 0);
+
+ return (len);
+}
diff --git a/usr/src/lib/libdtrace/common/dt_program.c b/usr/src/lib/libdtrace/common/dt_program.c
index 471e3c9096..e4f9d8dd1c 100644
--- a/usr/src/lib/libdtrace/common/dt_program.c
+++ b/usr/src/lib/libdtrace/common/dt_program.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <unistd.h>
@@ -356,6 +357,7 @@ dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
if (sdp->dtsd_fmtdata != NULL)
dt_printf_destroy(sdp->dtsd_fmtdata);
+ dt_free(dtp, sdp->dtsd_strdata);
dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
dt_free(dtp, sdp);
diff --git a/usr/src/lib/libdtrace/common/dtrace.h b/usr/src/lib/libdtrace/common/dtrace.h
index c1b31151da..d3031d8c4c 100644
--- a/usr/src/lib/libdtrace/common/dtrace.h
+++ b/usr/src/lib/libdtrace/common/dtrace.h
@@ -151,6 +151,7 @@ typedef struct dtrace_stmtdesc {
dtrace_actdesc_t *dtsd_action_last; /* last action in action list */
void *dtsd_aggdata; /* aggregation data */
void *dtsd_fmtdata; /* type-specific output data */
+ void *dtsd_strdata; /* type-specific string data */
void (*dtsd_callback)(); /* callback function for EPID */
void *dtsd_data; /* callback data pointer */
dtrace_attribute_t dtsd_descattr; /* probedesc attributes */
@@ -243,6 +244,18 @@ extern int dtrace_freopen(dtrace_hdl_t *, FILE *, void *,
const void *, size_t);
/*
+ * Type-specific output printing
+ *
+ * The print() action will associate a string data record that is actually the
+ * fully-qualified type name of the data traced by the DIFEXPR action. This is
+ * stored in the same 'format' record from the kernel, but we know by virtue of
+ * the fact that the action is still DIFEXPR that it is actually a reference to
+ * plain string data.
+ */
+extern int dtrace_print(dtrace_hdl_t *, FILE *, const char *,
+ caddr_t, size_t);
+
+/*
* DTrace Work Interface
*/
typedef enum {
diff --git a/usr/src/lib/libnsl/Makefile.com b/usr/src/lib/libnsl/Makefile.com
index cea06c2c54..8469102553 100644
--- a/usr/src/lib/libnsl/Makefile.com
+++ b/usr/src/lib/libnsl/Makefile.com
@@ -21,6 +21,7 @@
#
# Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
#
LIBRARY= libnsl.a
@@ -41,8 +42,8 @@ NETDIR= netdir.o
NSS= \
gethostbyname_r.o gethostent.o gethostent_r.o gethostent6.o gethostby_door.o \
-getipnodeby_door.o getipnodeby.o getrpcent.o getrpcent_r.o inet_pton.o \
-inet_ntop.o netdir_inet.o netdir_inet_sundry.o \
+getipnodeby_door.o getipnodeby.o getrpcent.o getrpcent_r.o inet_matchaddr.o \
+inet_pton.o inet_ntop.o netdir_inet.o netdir_inet_sundry.o \
parse.o getauthattr.o getprofattr.o getexecattr.o getuserattr.o getauuser.o
NETSELECT= netselect.o
diff --git a/usr/src/lib/libnsl/common/llib-lnsl b/usr/src/lib/libnsl/common/llib-lnsl
index 8e70bbd2c8..106dab70d6 100644
--- a/usr/src/lib/libnsl/common/llib-lnsl
+++ b/usr/src/lib/libnsl/common/llib-lnsl
@@ -22,6 +22,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
@@ -437,6 +439,9 @@ void setrpcent(const int stay);
void endrpcent(void);
struct rpcent *getrpcent_r(struct rpcent *result, char *buffer, int buflen);
+/* inet_matchaddr.c */
+boolean_t inet_matchaddr(const void *, const char *);
+
/* inet_ntop.c */
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
diff --git a/usr/src/lib/libnsl/common/mapfile-vers b/usr/src/lib/libnsl/common/mapfile-vers
index f150c1e905..f875cd99fc 100644
--- a/usr/src/lib/libnsl/common/mapfile-vers
+++ b/usr/src/lib/libnsl/common/mapfile-vers
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-#
+# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
#
#
@@ -467,6 +467,7 @@ $endif
SYMBOL_VERSION SUNWprivate_1.5 {
global:
+ inet_matchaddr;
clnt_create_service_timed;
} SUNWprivate_1.4;
diff --git a/usr/src/lib/libnsl/nss/inet_matchaddr.c b/usr/src/lib/libnsl/nss/inet_matchaddr.c
new file mode 100644
index 0000000000..566c1ae1ad
--- /dev/null
+++ b/usr/src/lib/libnsl/nss/inet_matchaddr.c
@@ -0,0 +1,150 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * inet_matchaddr
+ *
+ * Match IPv4 or IPv6 address provided in sa (sockaddr_in/sockaddr_in6)
+ * against standard text representation specified in name:
+ *
+ * IPv4:
+ * IPv4
+ * IPv4/netmask
+ *
+ * IPv6:
+ * [IPv6]
+ * [IPv6]/prefix
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <strings.h>
+
+
+boolean_t
+inet_matchaddr(const void *sa, const char *name)
+{
+ boolean_t ret = B_FALSE;
+ char *lname, *mp, *p;
+ uint32_t claddr4 = 0;
+
+ if ((p = lname = strdup(name)) == NULL)
+ err(1, "strdup");
+
+ if ((mp = strchr(p, '/')) != NULL)
+ *mp++ = '\0';
+
+ switch (((struct sockaddr_in *)sa)->sin_family) {
+ case AF_INET6: {
+ char *pp;
+ int prefix6;
+ ipaddr_t ipaddr4;
+ struct in6_addr hcaddr6;
+ struct in6_addr *claddr6 =
+ &((struct sockaddr_in6 *)sa)->sin6_addr;
+
+ if (!IN6_IS_ADDR_V4MAPPED(claddr6)) {
+ /* IPv6 address */
+ if ((p = strchr(p, '[')) == NULL)
+ break;
+ p++;
+
+ if ((pp = strchr(p, ']')) == NULL)
+ break;
+ *pp = '\0';
+
+ if (inet_pton(AF_INET6, p, &hcaddr6) != 1)
+ break;
+
+ if (mp != NULL) {
+ /* Match only first prefix bits */
+ if ((prefix6 = (int)strtol(mp,
+ (char **)NULL, 10)) == 0)
+ break;
+ ret = IN6_ARE_PREFIXEDADDR_EQUAL(claddr6,
+ &hcaddr6, prefix6);
+ break;
+ } else {
+ /* No prefix, exact match */
+ ret = IN6_ARE_ADDR_EQUAL(claddr6, &hcaddr6);
+ break;
+ }
+ } else {
+ /* IPv4-mapped IPv6 address, fallthrough to IPv4 */
+ IN6_V4MAPPED_TO_IPADDR(claddr6, ipaddr4);
+ claddr4 = ntohl(ipaddr4);
+ }
+ /*FALLTHROUGH*/
+ }
+ case AF_INET: {
+ int bits, i;
+ uint32_t hcaddr4 = 0, mask4;
+
+ if (claddr4 == 0)
+ claddr4 = ntohl(
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr);
+
+ for (i = 0; i < 4; i++) {
+ hcaddr4 |= (int)strtol(p, (char **)NULL, 10) <<
+ ((3 - i) * 8);
+ if ((p = strchr(p, '.')) == NULL)
+ break;
+ p++;
+ }
+
+ if (hcaddr4 == 0)
+ break;
+
+ if (mp != NULL) {
+ /* Mask is specified explicitly */
+ if ((bits = (int)strtol(mp, (char **)NULL, 10)) == 0)
+ break;
+ mask4 = bits ? ~0 << ((sizeof (struct in_addr) * NBBY)
+ - bits) : 0;
+ hcaddr4 &= mask4;
+ } else {
+ /*
+ * Use old-fashioned implicit netmasking by checking
+ * for lower-end zeroes. On the off chance we don't
+ * match any well-known prefixes, return an exact-
+ * match prefix which is misleadingly labelled as
+ * IN_CLASSE_NET.
+ */
+ if ((hcaddr4 & IN_CLASSA_HOST) == 0)
+ mask4 = IN_CLASSA_NET;
+ else if ((hcaddr4 & IN_CLASSB_HOST) == 0)
+ mask4 = IN_CLASSB_NET;
+ else if ((hcaddr4 & IN_CLASSC_HOST) == 0)
+ mask4 = IN_CLASSC_NET;
+ else
+ mask4 = IN_CLASSE_NET;
+ }
+
+ ret = ((claddr4 & mask4) == hcaddr4);
+ break;
+ }
+ }
+
+ free(lname);
+
+ return (ret);
+}
diff --git a/usr/src/lib/libsmbfs/netsmb/smbfs_api.h b/usr/src/lib/libsmbfs/netsmb/smbfs_api.h
index 8987f1a934..bc320ee8ab 100644
--- a/usr/src/lib/libsmbfs/netsmb/smbfs_api.h
+++ b/usr/src/lib/libsmbfs/netsmb/smbfs_api.h
@@ -90,7 +90,7 @@ typedef enum {
#define SMB_AT_NTLM1 4 /* NTLM (v1) */
#define SMB_AT_NTLM2 8 /* NTLMv2 */
#define SMB_AT_KRB5 0x10 /* Kerberos5 (AD) */
-#define SMB_AT_DEFAULT (SMB_AT_KRB5 | SMB_AT_NTLM2 | SMB_AT_NTLM1)
+#define SMB_AT_DEFAULT (SMB_AT_KRB5 | SMB_AT_NTLM2)
struct smb_ctx; /* anonymous here; real one in smb_lib.h */
typedef struct smb_ctx smb_ctx_t;
diff --git a/usr/src/lib/libsmbfs/smb/ntlmssp.c b/usr/src/lib/libsmbfs/smb/ntlmssp.c
index 687d91b355..5766ec835c 100644
--- a/usr/src/lib/libsmbfs/smb/ntlmssp.c
+++ b/usr/src/lib/libsmbfs/smb/ntlmssp.c
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -243,6 +244,11 @@ ntlmssp_put_type1(struct ssp_ctx *sp, struct mbdata *out_mb)
ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
}
+ if (ctx->ct_authflags & SMB_AT_NTLM2)
+ ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY;
+ if (ctx->ct_authflags & SMB_AT_NTLM1)
+ ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+
bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
hdr.h_type = 1; /* Type 1 */
hdr.h_flags = ssp_st->ss_flags;
diff --git a/usr/src/lib/libxnet/common/mapfile-vers b/usr/src/lib/libxnet/common/mapfile-vers
index 72d7871838..a284ae3311 100644
--- a/usr/src/lib/libxnet/common/mapfile-vers
+++ b/usr/src/lib/libxnet/common/mapfile-vers
@@ -39,8 +39,13 @@ $mapfile_version 2
SYMBOL_VERSION SUNW_1.4 {
global:
- freeaddrinfo { TYPE = FUNCTION; FILTER = libsocket.so.1 };
gai_strerror { TYPE = FUNCTION; FILTER = libsocket.so.1 };
+} SUNW_1.3.1;
+
+SYMBOL_VERSION SUNW_1.3.1 {
+ global:
+ __xnet_getaddrinfo { TYPE = FUNCTION; FILTER = libsocket.so.1 };
+ freeaddrinfo { TYPE = FUNCTION; FILTER = libsocket.so.1 };
getaddrinfo { TYPE = FUNCTION; FILTER = libsocket.so.1 };
getnameinfo { TYPE = FUNCTION; FILTER = libsocket.so.1 };
inet_ntop { TYPE = FUNCTION; FILTER = libnsl.so.1 };
diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com
index 77e2aa4467..44327f934b 100644
--- a/usr/src/lib/libzfs/Makefile.com
+++ b/usr/src/lib/libzfs/Makefile.com
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011 by Delphix. All rights reserved.
#
LIBRARY= libzfs.a
@@ -40,8 +41,8 @@ OBJS_COMMON= \
libzfs_dataset.o \
libzfs_diff.o \
libzfs_fru.o \
- libzfs_graph.o \
libzfs_import.o \
+ libzfs_iter.o \
libzfs_mount.o \
libzfs_pool.o \
libzfs_sendrecv.o \
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index 6c10dec621..3f093e3767 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -21,8 +21,9 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
* Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _LIBZFS_H
@@ -231,6 +232,7 @@ typedef struct splitflags {
*/
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t);
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
+extern int zpool_reguid(zpool_handle_t *);
extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
vdev_state_t *);
@@ -382,6 +384,7 @@ extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
* underlying datasets, only the references to them.
*/
extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
+extern zfs_handle_t *zfs_handle_dup(zfs_handle_t *);
extern void zfs_close(zfs_handle_t *);
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
extern const char *zfs_get_name(const zfs_handle_t *);
@@ -415,12 +418,20 @@ extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
uint64_t *propvalue);
extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
char *propbuf, int proplen, boolean_t literal);
+extern int zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
+ uint64_t *propvalue);
+extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
+ char *propbuf, int proplen, boolean_t literal);
+extern int zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
+ uint64_t *usedp);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
extern const char *zfs_prop_values(zfs_prop_t);
extern int zfs_prop_is_string(zfs_prop_t prop);
extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *);
+extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *);
+
typedef struct zprop_list {
int pl_prop;
@@ -495,6 +506,7 @@ extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
+extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
typedef struct get_all_cb {
zfs_handle_t **cb_handles;
@@ -515,6 +527,7 @@ extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
extern int zfs_destroy(zfs_handle_t *, boolean_t);
extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
+extern int zfs_destroy_snaps_nvl(zfs_handle_t *, nvlist_t *, boolean_t);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
@@ -522,29 +535,34 @@ extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
typedef struct sendflags {
/* print informational messages (ie, -v was specified) */
- int verbose : 1;
+ boolean_t verbose;
/* recursive send (ie, -R) */
- int replicate : 1;
+ boolean_t replicate;
/* for incrementals, do all intermediate snapshots */
- int doall : 1; /* (ie, -I) */
+ boolean_t doall;
/* if dataset is a clone, do incremental from its origin */
- int fromorigin : 1;
+ boolean_t fromorigin;
/* do deduplication */
- int dedup : 1;
+ boolean_t dedup;
/* send properties (ie, -p) */
- int props : 1;
+ boolean_t props;
+
+ /* do not send (no-op, ie. -n) */
+ boolean_t dryrun;
+
+ /* parsable verbose output (ie. -P) */
+ boolean_t parsable;
} sendflags_t;
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
-extern int zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
- void *cb_arg, nvlist_t **debugnvp);
+extern int zfs_send(zfs_handle_t *, const char *, const char *,
+ sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
extern int zfs_promote(zfs_handle_t *);
extern int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t,
@@ -564,34 +582,34 @@ extern int zfs_set_fsacl(zfs_handle_t *, boolean_t, nvlist_t *);
typedef struct recvflags {
/* print informational messages (ie, -v was specified) */
- int verbose : 1;
+ boolean_t verbose;
/* the destination is a prefix, not the exact fs (ie, -d) */
- int isprefix : 1;
+ boolean_t isprefix;
/*
* Only the tail of the sent snapshot path is appended to the
* destination to determine the received snapshot name (ie, -e).
*/
- int istail : 1;
+ boolean_t istail;
/* do not actually do the recv, just check if it would work (ie, -n) */
- int dryrun : 1;
+ boolean_t dryrun;
/* rollback/destroy filesystems as necessary (eg, -F) */
- int force : 1;
+ boolean_t force;
/* set "canmount=off" on all modified filesystems */
- int canmountoff : 1;
+ boolean_t canmountoff;
/* byteswap flag is used internally; callers need not specify */
- int byteswap : 1;
+ boolean_t byteswap;
/* do not mount file systems as they are extracted (private) */
- int nomount : 1;
+ boolean_t nomount;
} recvflags_t;
-extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t,
+extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *,
int, avl_tree_t *);
typedef enum diff_flags {
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index b2508895a8..43cff1f7e7 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -497,7 +497,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
return (zhp);
}
-static zfs_handle_t *
+zfs_handle_t *
make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
{
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
@@ -514,6 +514,53 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
return (zhp);
}
+zfs_handle_t *
+zfs_handle_dup(zfs_handle_t *zhp_orig)
+{
+ zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+
+ if (zhp == NULL)
+ return (NULL);
+
+ zhp->zfs_hdl = zhp_orig->zfs_hdl;
+ zhp->zpool_hdl = zhp_orig->zpool_hdl;
+ (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
+ sizeof (zhp->zfs_name));
+ zhp->zfs_type = zhp_orig->zfs_type;
+ zhp->zfs_head_type = zhp_orig->zfs_head_type;
+ zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
+ if (zhp_orig->zfs_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ if (zhp_orig->zfs_user_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_user_props,
+ &zhp->zfs_user_props, 0) != 0) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ if (zhp_orig->zfs_recvd_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_recvd_props,
+ &zhp->zfs_recvd_props, 0)) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
+ if (zhp_orig->zfs_mntopts != NULL) {
+ zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
+ zhp_orig->zfs_mntopts);
+ }
+ zhp->zfs_props_table = zhp_orig->zfs_props_table;
+ return (zhp);
+}
+
/*
* Opens the given snapshot, filesystem, or volume. The 'types'
* argument is a mask of acceptable types. The function will print an
@@ -877,6 +924,12 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
goto error;
}
continue;
+ } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' is readonly"),
+ propname);
+ (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
+ goto error;
}
if (prop == ZPROP_INVAL) {
@@ -1850,8 +1903,6 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
err = zfs_prop_get(zhp, prop, propbuf, proplen,
NULL, NULL, 0, literal);
zfs_unset_recvd_props_mode(zhp, &cookie);
- } else if (zfs_prop_userquota(propname)) {
- return (-1);
} else {
nvlist_t *propval;
char *recvdval;
@@ -1866,6 +1917,120 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
return (err == 0 ? 0 : -1);
}
+static int
+get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
+{
+ nvlist_t *value;
+ nvpair_t *pair;
+
+ value = zfs_get_clones_nvl(zhp);
+ if (value == NULL)
+ return (-1);
+
+ propbuf[0] = '\0';
+ for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(value, pair)) {
+ if (propbuf[0] != '\0')
+ (void) strlcat(propbuf, ",", proplen);
+ (void) strlcat(propbuf, nvpair_name(pair), proplen);
+ }
+
+ return (0);
+}
+
+struct get_clones_arg {
+ uint64_t numclones;
+ nvlist_t *value;
+ const char *origin;
+ char buf[ZFS_MAXNAMELEN];
+};
+
+int
+get_clones_cb(zfs_handle_t *zhp, void *arg)
+{
+ struct get_clones_arg *gca = arg;
+
+ if (gca->numclones == 0) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
+ NULL, NULL, 0, B_TRUE) != 0)
+ goto out;
+ if (strcmp(gca->buf, gca->origin) == 0) {
+ if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) != 0) {
+ zfs_close(zhp);
+ return (no_memory(zhp->zfs_hdl));
+ }
+ gca->numclones--;
+ }
+
+out:
+ (void) zfs_iter_children(zhp, get_clones_cb, gca);
+ zfs_close(zhp);
+ return (0);
+}
+
+nvlist_t *
+zfs_get_clones_nvl(zfs_handle_t *zhp)
+{
+ nvlist_t *nv, *value;
+
+ if (nvlist_lookup_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
+ struct get_clones_arg gca;
+
+ /*
+ * if this is a snapshot, then the kernel wasn't able
+ * to get the clones. Do it by slowly iterating.
+ */
+ if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
+ return (NULL);
+ if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
+ return (NULL);
+ if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
+ nvlist_free(nv);
+ return (NULL);
+ }
+
+ gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
+ gca.value = value;
+ gca.origin = zhp->zfs_name;
+
+ if (gca.numclones != 0) {
+ zfs_handle_t *root;
+ char pool[ZFS_MAXNAMELEN];
+ char *cp = pool;
+
+ /* get the pool name */
+ (void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
+ (void) strsep(&cp, "/@");
+ root = zfs_open(zhp->zfs_hdl, pool,
+ ZFS_TYPE_FILESYSTEM);
+
+ (void) get_clones_cb(root, &gca);
+ }
+
+ if (gca.numclones != 0 ||
+ nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
+ nvlist_add_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
+ nvlist_free(nv);
+ nvlist_free(value);
+ return (NULL);
+ }
+ nvlist_free(nv);
+ nvlist_free(value);
+ verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
+ }
+
+ verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
+
+ return (value);
+}
+
/*
* Retrieve a property from the given object. If 'literal' is specified, then
* numbers are left as exact values. Otherwise, numbers are converted to a
@@ -1994,6 +2159,11 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
return (-1);
break;
+ case ZFS_PROP_CLONES:
+ if (get_clones_string(zhp, propbuf, proplen) != 0)
+ return (-1);
+ break;
+
case ZFS_PROP_QUOTA:
case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
@@ -2354,7 +2524,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
int err;
zfs_cmd_t zc = { 0 };
- (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
err = userquota_propname_decode(propname,
zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
@@ -2406,144 +2576,95 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
return (0);
}
-/*
- * Returns the name of the given zfs handle.
- */
-const char *
-zfs_get_name(const zfs_handle_t *zhp)
+int
+zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
+ uint64_t *propvalue)
{
- return (zhp->zfs_name);
-}
+ int err;
+ zfs_cmd_t zc = { 0 };
+ const char *snapname;
-/*
- * Returns the type of the given zfs handle.
- */
-zfs_type_t
-zfs_get_type(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_type);
-}
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-static int
-zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
-{
- int rc;
- uint64_t orig_cookie;
+ snapname = strchr(propname, '@') + 1;
+ if (strchr(snapname, '@')) {
+ (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+ } else {
+ /* snapname is the short name, append it to zhp's fsname */
+ char *cp;
+
+ (void) strlcpy(zc.zc_value, zhp->zfs_name,
+ sizeof (zc.zc_value));
+ cp = strchr(zc.zc_value, '@');
+ if (cp != NULL)
+ *cp = '\0';
+ (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
+ (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
+ }
- orig_cookie = zc->zc_cookie;
-top:
- (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
- rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
+ err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
+ if (err)
+ return (err);
- if (rc == -1) {
- switch (errno) {
- case ENOMEM:
- /* expand nvlist memory and try again */
- if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
- zcmd_free_nvlists(zc);
- return (-1);
- }
- zc->zc_cookie = orig_cookie;
- goto top;
- /*
- * An errno value of ESRCH indicates normal completion.
- * If ENOENT is returned, then the underlying dataset
- * has been removed since we obtained the handle.
- */
- case ESRCH:
- case ENOENT:
- rc = 1;
- break;
- default:
- rc = zfs_standard_error(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN,
- "cannot iterate filesystems"));
- break;
- }
- }
- return (rc);
+ *propvalue = zc.zc_cookie;
+ return (0);
}
-/*
- * Iterate over all child filesystems
- */
int
-zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
+ char *propbuf, int proplen, boolean_t literal)
{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
-
- if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
- return (0);
+ int err;
+ uint64_t propvalue;
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
+ err = zfs_prop_get_written_int(zhp, propname, &propvalue);
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
- &zc)) == 0) {
- /*
- * Silently ignore errors, as the only plausible explanation is
- * that the pool has since been removed.
- */
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
+ if (err)
+ return (err);
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
+ if (literal) {
+ (void) snprintf(propbuf, proplen, "%llu", propvalue);
+ } else {
+ zfs_nicenum(propvalue, propbuf, proplen);
}
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+ return (0);
}
-/*
- * Iterate over all snapshots
- */
int
-zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
+ uint64_t *usedp)
{
+ int err;
zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
- return (0);
+ (void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
- &zc)) == 0) {
+ err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
+ if (err)
+ return (err);
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
+ *usedp = zc.zc_cookie;
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+ return (0);
}
/*
- * Iterate over all children, snapshots and filesystems
+ * Returns the name of the given zfs handle.
*/
-int
-zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+const char *
+zfs_get_name(const zfs_handle_t *zhp)
{
- int ret;
-
- if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
- return (ret);
+ return (zhp->zfs_name);
+}
- return (zfs_iter_snapshots(zhp, func, data));
+/*
+ * Returns the type of the given zfs handle.
+ */
+zfs_type_t
+zfs_get_type(const zfs_handle_t *zhp)
+{
+ return (zhp->zfs_type);
}
/*
@@ -2569,18 +2690,19 @@ is_descendant(const char *ds1, const char *ds2)
/*
* Given a complete name, return just the portion that refers to the parent.
- * Can return NULL if this is a pool.
+ * Will return -1 if there is no parent (path is just the name of the
+ * pool).
*/
static int
parent_name(const char *path, char *buf, size_t buflen)
{
- char *loc;
+ char *slashp;
- if ((loc = strrchr(path, '/')) == NULL)
- return (-1);
+ (void) strlcpy(buf, path, buflen);
- (void) strncpy(buf, path, MIN(buflen, loc - path));
- buf[loc - path] = '\0';
+ if ((slashp = strrchr(buf, '/')) == NULL)
+ return (-1);
+ *slashp = '\0';
return (0);
}
@@ -2979,9 +3101,8 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
}
struct destroydata {
- char *snapname;
- boolean_t gotone;
- boolean_t closezhp;
+ nvlist_t *nvl;
+ const char *snapname;
};
static int
@@ -2990,24 +3111,19 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
struct destroydata *dd = arg;
zfs_handle_t *szhp;
char name[ZFS_MAXNAMELEN];
- boolean_t closezhp = dd->closezhp;
int rv = 0;
- (void) strlcpy(name, zhp->zfs_name, sizeof (name));
- (void) strlcat(name, "@", sizeof (name));
- (void) strlcat(name, dd->snapname, sizeof (name));
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zhp->zfs_name, dd->snapname);
szhp = make_dataset_handle(zhp->zfs_hdl, name);
if (szhp) {
- dd->gotone = B_TRUE;
+ verify(nvlist_add_boolean(dd->nvl, name) == 0);
zfs_close(szhp);
}
- dd->closezhp = B_TRUE;
- if (!dd->gotone)
- rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
- if (closezhp)
- zfs_close(zhp);
+ rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
+ zfs_close(zhp);
return (rv);
}
@@ -3017,29 +3133,45 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
int
zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
{
- zfs_cmd_t zc = { 0 };
int ret;
struct destroydata dd = { 0 };
dd.snapname = snapname;
- (void) zfs_check_snap_cb(zhp, &dd);
+ verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
+ (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
- if (!dd.gotone) {
- return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
+ if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) {
+ ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
- zhp->zfs_name, snapname));
+ zhp->zfs_name, snapname);
+ } else {
+ ret = zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
}
+ nvlist_free(dd.nvl);
+ return (ret);
+}
+
+/*
+ * Destroys all the snapshots named in the nvlist. They must be underneath
+ * the zhp (either snapshots of it, or snapshots of its descendants).
+ */
+int
+zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
+{
+ int ret;
+ zfs_cmd_t zc = { 0 };
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+ if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
+ return (-1);
zc.zc_defer_destroy = defer;
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
if (ret != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot destroy '%s@%s'"), zc.zc_name, snapname);
+ "cannot destroy snapshots in %s"), zc.zc_name);
switch (errno) {
case EEXIST:
@@ -3075,7 +3207,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), target);
- /* validate the target name */
+ /* validate the target/clone name */
if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
@@ -3412,42 +3544,6 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
}
/*
- * Iterate over all dependents for a given dataset. This includes both
- * hierarchical dependents (children) and data dependents (snapshots and
- * clones). The bulk of the processing occurs in get_dependents() in
- * libzfs_graph.c.
- */
-int
-zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
- zfs_iter_f func, void *data)
-{
- char **dependents;
- size_t count;
- int i;
- zfs_handle_t *child;
- int ret = 0;
-
- if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
- &dependents, &count) != 0)
- return (-1);
-
- for (i = 0; i < count; i++) {
- if ((child = make_dataset_handle(zhp->zfs_hdl,
- dependents[i])) == NULL)
- continue;
-
- if ((ret = func(child, data)) != 0)
- break;
- }
-
- for (i = 0; i < count; i++)
- free(dependents[i]);
- free(dependents);
-
- return (ret);
-}
-
-/*
* Renames the given dataset.
*/
int
@@ -3896,7 +3992,7 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
int error;
zfs_useracct_t buf[100];
- (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
zc.zc_objset_type = type;
zc.zc_nvlist_dst = (uintptr_t)buf;
diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c
deleted file mode 100644
index bc21c51ae2..0000000000
--- a/usr/src/lib/libzfs/common/libzfs_graph.c
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * 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
- */
-/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * Iterate over all children of the current object. This includes the normal
- * dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
- * walk all datasets in the pool, and construct a directed graph of the form:
- *
- * home
- * |
- * +----+----+
- * | |
- * v v ws
- * bar baz |
- * | |
- * v v
- * @yesterday ----> foo
- *
- * In order to construct this graph, we have to walk every dataset in the pool,
- * because the clone parent is stored as a property of the child, not the
- * parent. The parent only keeps track of the number of clones.
- *
- * In the normal case (without clones) this would be rather expensive. To avoid
- * unnecessary computation, we first try a walk of the subtree hierarchy
- * starting from the initial node. At each dataset, we construct a node in the
- * graph and an edge leading from its parent. If we don't see any snapshots
- * with a non-zero clone count, then we are finished.
- *
- * If we do find a cloned snapshot, then we finish the walk of the current
- * subtree, but indicate that we need to do a complete walk. We then perform a
- * global walk of all datasets, avoiding the subtree we already processed.
- *
- * At the end of this, we'll end up with a directed graph of all relevant (and
- * possible some irrelevant) datasets in the system. We need to both find our
- * limiting subgraph and determine a safe ordering in which to destroy the
- * datasets. We do a topological ordering of our graph starting at our target
- * dataset, and then walk the results in reverse.
- *
- * It's possible for the graph to have cycles if, for example, the user renames
- * a clone to be the parent of its origin snapshot. The user can request to
- * generate an error in this case, or ignore the cycle and continue.
- *
- * When removing datasets, we want to destroy the snapshots in chronological
- * order (because this is the most efficient method). In order to accomplish
- * this, we store the creation transaction group with each vertex and keep each
- * vertex's edges sorted according to this value. The topological sort will
- * automatically walk the snapshots in the correct order.
- */
-
-#include <assert.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-#include "zfs_namecheck.h"
-
-#define MIN_EDGECOUNT 4
-
-/*
- * Vertex structure. Indexed by dataset name, this structure maintains a list
- * of edges to other vertices.
- */
-struct zfs_edge;
-typedef struct zfs_vertex {
- char zv_dataset[ZFS_MAXNAMELEN];
- struct zfs_vertex *zv_next;
- int zv_visited;
- uint64_t zv_txg;
- struct zfs_edge **zv_edges;
- int zv_edgecount;
- int zv_edgealloc;
-} zfs_vertex_t;
-
-enum {
- VISIT_SEEN = 1,
- VISIT_SORT_PRE,
- VISIT_SORT_POST
-};
-
-/*
- * Edge structure. Simply maintains a pointer to the destination vertex. There
- * is no need to store the source vertex, since we only use edges in the context
- * of the source vertex.
- */
-typedef struct zfs_edge {
- zfs_vertex_t *ze_dest;
- struct zfs_edge *ze_next;
-} zfs_edge_t;
-
-#define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */
-
-/*
- * Graph structure. Vertices are maintained in a hash indexed by dataset name.
- */
-typedef struct zfs_graph {
- zfs_vertex_t **zg_hash;
- size_t zg_size;
- size_t zg_nvertex;
- const char *zg_root;
- int zg_clone_count;
-} zfs_graph_t;
-
-/*
- * Allocate a new edge pointing to the target vertex.
- */
-static zfs_edge_t *
-zfs_edge_create(libzfs_handle_t *hdl, zfs_vertex_t *dest)
-{
- zfs_edge_t *zep = zfs_alloc(hdl, sizeof (zfs_edge_t));
-
- if (zep == NULL)
- return (NULL);
-
- zep->ze_dest = dest;
-
- return (zep);
-}
-
-/*
- * Destroy an edge.
- */
-static void
-zfs_edge_destroy(zfs_edge_t *zep)
-{
- free(zep);
-}
-
-/*
- * Allocate a new vertex with the given name.
- */
-static zfs_vertex_t *
-zfs_vertex_create(libzfs_handle_t *hdl, const char *dataset)
-{
- zfs_vertex_t *zvp = zfs_alloc(hdl, sizeof (zfs_vertex_t));
-
- if (zvp == NULL)
- return (NULL);
-
- assert(strlen(dataset) < ZFS_MAXNAMELEN);
-
- (void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset));
-
- if ((zvp->zv_edges = zfs_alloc(hdl,
- MIN_EDGECOUNT * sizeof (void *))) == NULL) {
- free(zvp);
- return (NULL);
- }
-
- zvp->zv_edgealloc = MIN_EDGECOUNT;
-
- return (zvp);
-}
-
-/*
- * Destroy a vertex. Frees up any associated edges.
- */
-static void
-zfs_vertex_destroy(zfs_vertex_t *zvp)
-{
- int i;
-
- for (i = 0; i < zvp->zv_edgecount; i++)
- zfs_edge_destroy(zvp->zv_edges[i]);
-
- free(zvp->zv_edges);
- free(zvp);
-}
-
-/*
- * Given a vertex, add an edge to the destination vertex.
- */
-static int
-zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp,
- zfs_vertex_t *dest)
-{
- zfs_edge_t *zep = zfs_edge_create(hdl, dest);
-
- if (zep == NULL)
- return (-1);
-
- if (zvp->zv_edgecount == zvp->zv_edgealloc) {
- void *ptr;
-
- if ((ptr = zfs_realloc(hdl, zvp->zv_edges,
- zvp->zv_edgealloc * sizeof (void *),
- zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL)
- return (-1);
-
- zvp->zv_edges = ptr;
- zvp->zv_edgealloc *= 2;
- }
-
- zvp->zv_edges[zvp->zv_edgecount++] = zep;
-
- return (0);
-}
-
-static int
-zfs_edge_compare(const void *a, const void *b)
-{
- const zfs_edge_t *ea = *((zfs_edge_t **)a);
- const zfs_edge_t *eb = *((zfs_edge_t **)b);
-
- if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg)
- return (-1);
- if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg)
- return (1);
- return (0);
-}
-
-/*
- * Sort the given vertex edges according to the creation txg of each vertex.
- */
-static void
-zfs_vertex_sort_edges(zfs_vertex_t *zvp)
-{
- if (zvp->zv_edgecount == 0)
- return;
-
- qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *),
- zfs_edge_compare);
-}
-
-/*
- * Construct a new graph object. We allow the size to be specified as a
- * parameter so in the future we can size the hash according to the number of
- * datasets in the pool.
- */
-static zfs_graph_t *
-zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
-{
- zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
-
- if (zgp == NULL)
- return (NULL);
-
- zgp->zg_size = size;
- if ((zgp->zg_hash = zfs_alloc(hdl,
- size * sizeof (zfs_vertex_t *))) == NULL) {
- free(zgp);
- return (NULL);
- }
-
- zgp->zg_root = dataset;
- zgp->zg_clone_count = 0;
-
- return (zgp);
-}
-
-/*
- * Destroy a graph object. We have to iterate over all the hash chains,
- * destroying each vertex in the process.
- */
-static void
-zfs_graph_destroy(zfs_graph_t *zgp)
-{
- int i;
- zfs_vertex_t *current, *next;
-
- for (i = 0; i < zgp->zg_size; i++) {
- current = zgp->zg_hash[i];
- while (current != NULL) {
- next = current->zv_next;
- zfs_vertex_destroy(current);
- current = next;
- }
- }
-
- free(zgp->zg_hash);
- free(zgp);
-}
-
-/*
- * Graph hash function. Classic bernstein k=33 hash function, taken from
- * usr/src/cmd/sgs/tools/common/strhash.c
- */
-static size_t
-zfs_graph_hash(zfs_graph_t *zgp, const char *str)
-{
- size_t hash = 5381;
- int c;
-
- while ((c = *str++) != 0)
- hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
-
- return (hash % zgp->zg_size);
-}
-
-/*
- * Given a dataset name, finds the associated vertex, creating it if necessary.
- */
-static zfs_vertex_t *
-zfs_graph_lookup(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset,
- uint64_t txg)
-{
- size_t idx = zfs_graph_hash(zgp, dataset);
- zfs_vertex_t *zvp;
-
- for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) {
- if (strcmp(zvp->zv_dataset, dataset) == 0) {
- if (zvp->zv_txg == 0)
- zvp->zv_txg = txg;
- return (zvp);
- }
- }
-
- if ((zvp = zfs_vertex_create(hdl, dataset)) == NULL)
- return (NULL);
-
- zvp->zv_next = zgp->zg_hash[idx];
- zvp->zv_txg = txg;
- zgp->zg_hash[idx] = zvp;
- zgp->zg_nvertex++;
-
- return (zvp);
-}
-
-/*
- * Given two dataset names, create an edge between them. For the source vertex,
- * mark 'zv_visited' to indicate that we have seen this vertex, and not simply
- * created it as a destination of another edge. If 'dest' is NULL, then this
- * is an individual vertex (i.e. the starting vertex), so don't add an edge.
- */
-static int
-zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
- const char *dest, uint64_t txg)
-{
- zfs_vertex_t *svp, *dvp;
-
- if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
- return (-1);
- svp->zv_visited = VISIT_SEEN;
- if (dest != NULL) {
- dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
- if (dvp == NULL)
- return (-1);
- if (zfs_vertex_add_edge(hdl, svp, dvp) != 0)
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Iterate over all children of the given dataset, adding any vertices
- * as necessary. Returns -1 if there was an error, or 0 otherwise.
- * This is a simple recursive algorithm - the ZFS namespace typically
- * is very flat. We manually invoke the necessary ioctl() calls to
- * avoid the overhead and additional semantics of zfs_open().
- */
-static int
-iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
-{
- zfs_cmd_t zc = { 0 };
- zfs_vertex_t *zvp;
-
- /*
- * Look up the source vertex, and avoid it if we've seen it before.
- */
- zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
- if (zvp == NULL)
- return (-1);
- if (zvp->zv_visited == VISIT_SEEN)
- return (0);
-
- /*
- * Iterate over all children
- */
- for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
- /*
- * Get statistics for this dataset, to determine the type of the
- * dataset and clone statistics. If this fails, the dataset has
- * since been removed, and we're pretty much screwed anyway.
- */
- zc.zc_objset_stats.dds_origin[0] = '\0';
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
- continue;
-
- if (zc.zc_objset_stats.dds_origin[0] != '\0') {
- if (zfs_graph_add(hdl, zgp,
- zc.zc_objset_stats.dds_origin, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
- /*
- * Count origins only if they are contained in the graph
- */
- if (isa_child_of(zc.zc_objset_stats.dds_origin,
- zgp->zg_root))
- zgp->zg_clone_count--;
- }
-
- /*
- * Add an edge between the parent and the child.
- */
- if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
-
- /*
- * Recursively visit child
- */
- if (iterate_children(hdl, zgp, zc.zc_name))
- return (-1);
- }
-
- /*
- * Now iterate over all snapshots.
- */
- bzero(&zc, sizeof (zc));
-
- for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
-
- /*
- * Get statistics for this dataset, to determine the type of the
- * dataset and clone statistics. If this fails, the dataset has
- * since been removed, and we're pretty much screwed anyway.
- */
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
- continue;
-
- /*
- * Add an edge between the parent and the child.
- */
- if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
-
- zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
- }
-
- zvp->zv_visited = VISIT_SEEN;
-
- return (0);
-}
-
-/*
- * Returns false if there are no snapshots with dependent clones in this
- * subtree or if all of those clones are also in this subtree. Returns
- * true if there is an error or there are external dependents.
- */
-static boolean_t
-external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
-{
- zfs_cmd_t zc = { 0 };
-
- /*
- * Check whether this dataset is a clone or has clones since
- * iterate_children() only checks the children.
- */
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
- return (B_TRUE);
-
- if (zc.zc_objset_stats.dds_origin[0] != '\0') {
- if (zfs_graph_add(hdl, zgp,
- zc.zc_objset_stats.dds_origin, zc.zc_name,
- zc.zc_objset_stats.dds_creation_txg) != 0)
- return (B_TRUE);
- if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
- zgp->zg_clone_count--;
- }
-
- if ((zc.zc_objset_stats.dds_num_clones) ||
- iterate_children(hdl, zgp, dataset))
- return (B_TRUE);
-
- return (zgp->zg_clone_count != 0);
-}
-
-/*
- * Construct a complete graph of all necessary vertices. First, iterate over
- * only our object's children. If no cloned snapshots are found, or all of
- * the cloned snapshots are in this subtree then return a graph of the subtree.
- * Otherwise, start at the root of the pool and iterate over all datasets.
- */
-static zfs_graph_t *
-construct_graph(libzfs_handle_t *hdl, const char *dataset)
-{
- zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
- int ret = 0;
-
- if (zgp == NULL)
- return (zgp);
-
- if ((strchr(dataset, '/') == NULL) ||
- (external_dependents(hdl, zgp, dataset))) {
- /*
- * Determine pool name and try again.
- */
- int len = strcspn(dataset, "/@") + 1;
- char *pool = zfs_alloc(hdl, len);
-
- if (pool == NULL) {
- zfs_graph_destroy(zgp);
- return (NULL);
- }
- (void) strlcpy(pool, dataset, len);
-
- if (iterate_children(hdl, zgp, pool) == -1 ||
- zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
- free(pool);
- zfs_graph_destroy(zgp);
- return (NULL);
- }
- free(pool);
- }
-
- if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) {
- zfs_graph_destroy(zgp);
- return (NULL);
- }
-
- return (zgp);
-}
-
-/*
- * Given a graph, do a recursive topological sort into the given array. This is
- * really just a depth first search, so that the deepest nodes appear first.
- * hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
- */
-static int
-topo_sort(libzfs_handle_t *hdl, boolean_t allowrecursion, char **result,
- size_t *idx, zfs_vertex_t *zgv)
-{
- int i;
-
- if (zgv->zv_visited == VISIT_SORT_PRE && !allowrecursion) {
- /*
- * If we've already seen this vertex as part of our depth-first
- * search, then we have a cyclic dependency, and we must return
- * an error.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "recursive dependency at '%s'"),
- zgv->zv_dataset);
- return (zfs_error(hdl, EZFS_RECURSIVE,
- dgettext(TEXT_DOMAIN,
- "cannot determine dependent datasets")));
- } else if (zgv->zv_visited >= VISIT_SORT_PRE) {
- /*
- * If we've already processed this as part of the topological
- * sort, then don't bother doing so again.
- */
- return (0);
- }
-
- zgv->zv_visited = VISIT_SORT_PRE;
-
- /* avoid doing a search if we don't have to */
- zfs_vertex_sort_edges(zgv);
- for (i = 0; i < zgv->zv_edgecount; i++) {
- if (topo_sort(hdl, allowrecursion, result, idx,
- zgv->zv_edges[i]->ze_dest) != 0)
- return (-1);
- }
-
- /* we may have visited this in the course of the above */
- if (zgv->zv_visited == VISIT_SORT_POST)
- return (0);
-
- if ((result[*idx] = zfs_alloc(hdl,
- strlen(zgv->zv_dataset) + 1)) == NULL)
- return (-1);
-
- (void) strcpy(result[*idx], zgv->zv_dataset);
- *idx += 1;
- zgv->zv_visited = VISIT_SORT_POST;
- return (0);
-}
-
-/*
- * The only public interface for this file. Do the dirty work of constructing a
- * child list for the given object. Construct the graph, do the toplogical
- * sort, and then return the array of strings to the caller.
- *
- * The 'allowrecursion' parameter controls behavior when cycles are found. If
- * it is set, the the cycle is ignored and the results returned as if the cycle
- * did not exist. If it is not set, then the routine will generate an error if
- * a cycle is found.
- */
-int
-get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion,
- const char *dataset, char ***result, size_t *count)
-{
- zfs_graph_t *zgp;
- zfs_vertex_t *zvp;
-
- if ((zgp = construct_graph(hdl, dataset)) == NULL)
- return (-1);
-
- if ((*result = zfs_alloc(hdl,
- zgp->zg_nvertex * sizeof (char *))) == NULL) {
- zfs_graph_destroy(zgp);
- return (-1);
- }
-
- if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) {
- free(*result);
- zfs_graph_destroy(zgp);
- return (-1);
- }
-
- *count = 0;
- if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) {
- free(*result);
- zfs_graph_destroy(zgp);
- return (-1);
- }
-
- /*
- * Get rid of the last entry, which is our starting vertex and not
- * strictly a dependent.
- */
- assert(*count > 0);
- free((*result)[*count - 1]);
- (*count)--;
-
- zfs_graph_destroy(zgp);
-
- return (0);
-}
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index c9b09a2050..b1eae47ed2 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _LIBFS_IMPL_H
@@ -115,7 +116,7 @@ struct zpool_handle {
diskaddr_t zpool_start_block;
};
-typedef enum {
+typedef enum {
PROTO_NFS = 0,
PROTO_SMB = 1,
PROTO_END = 2
@@ -147,6 +148,7 @@ int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
size_t *);
+zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c
index e1370350fd..414aa2f747 100644
--- a/usr/src/lib/libzfs/common/libzfs_import.c
+++ b/usr/src/lib/libzfs/common/libzfs_import.c
@@ -20,6 +20,8 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
@@ -437,7 +439,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
uint_t i, nspares, nl2cache;
boolean_t config_seen;
uint64_t best_txg;
- char *name, *hostname;
+ char *name, *hostname, *comment;
uint64_t version, guid;
uint_t children = 0;
nvlist_t **child = NULL;
@@ -526,6 +528,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
* version
* pool guid
* name
+ * comment (if available)
* pool state
* hostid (if available)
* hostname (if available)
@@ -547,11 +550,24 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
if (nvlist_add_string(config,
ZPOOL_CONFIG_POOL_NAME, name) != 0)
goto nomem;
+
+ /*
+ * COMMENT is optional, don't bail if it's not
+ * there, instead, set it to NULL.
+ */
+ if (nvlist_lookup_string(tmp,
+ ZPOOL_CONFIG_COMMENT, &comment) != 0)
+ comment = NULL;
+ else if (nvlist_add_string(config,
+ ZPOOL_CONFIG_COMMENT, comment) != 0)
+ goto nomem;
+
verify(nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
if (nvlist_add_uint64(config,
ZPOOL_CONFIG_POOL_STATE, state) != 0)
goto nomem;
+
hostid = 0;
if (nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
diff --git a/usr/src/lib/libzfs/common/libzfs_iter.c b/usr/src/lib/libzfs/common/libzfs_iter.c
new file mode 100644
index 0000000000..212383d0e6
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_iter.c
@@ -0,0 +1,462 @@
+/*
+ * 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
+ */
+
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <libintl.h>
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+
+int
+zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ nvlist_t *nvl = zfs_get_clones_nvl(zhp);
+ nvpair_t *pair;
+
+ if (nvl == NULL)
+ return (0);
+
+ for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(nvl, pair)) {
+ zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (clone != NULL) {
+ int err = func(clone, data);
+ if (err != 0)
+ return (err);
+ }
+ }
+ return (0);
+}
+
+static int
+zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
+{
+ int rc;
+ uint64_t orig_cookie;
+
+ orig_cookie = zc->zc_cookie;
+top:
+ (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
+ rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
+
+ if (rc == -1) {
+ switch (errno) {
+ case ENOMEM:
+ /* expand nvlist memory and try again */
+ if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
+ zcmd_free_nvlists(zc);
+ return (-1);
+ }
+ zc->zc_cookie = orig_cookie;
+ goto top;
+ /*
+ * An errno value of ESRCH indicates normal completion.
+ * If ENOENT is returned, then the underlying dataset
+ * has been removed since we obtained the handle.
+ */
+ case ESRCH:
+ case ENOENT:
+ rc = 1;
+ break;
+ default:
+ rc = zfs_standard_error(zhp->zfs_hdl, errno,
+ dgettext(TEXT_DOMAIN,
+ "cannot iterate filesystems"));
+ break;
+ }
+ }
+ return (rc);
+}
+
+/*
+ * Iterate over all child filesystems
+ */
+int
+zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *nzhp;
+ int ret;
+
+ if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
+ return (0);
+
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
+
+ while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
+ &zc)) == 0) {
+ /*
+ * Silently ignore errors, as the only plausible explanation is
+ * that the pool has since been removed.
+ */
+ if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
+ &zc)) == NULL) {
+ continue;
+ }
+
+ if ((ret = func(nzhp, data)) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (ret);
+ }
+ }
+ zcmd_free_nvlists(&zc);
+ return ((ret < 0) ? ret : 0);
+}
+
+/*
+ * Iterate over all snapshots
+ */
+int
+zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *nzhp;
+ int ret;
+
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+ return (0);
+
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
+ while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
+ &zc)) == 0) {
+
+ if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
+ &zc)) == NULL) {
+ continue;
+ }
+
+ if ((ret = func(nzhp, data)) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (ret);
+ }
+ }
+ zcmd_free_nvlists(&zc);
+ return ((ret < 0) ? ret : 0);
+}
+
+/*
+ * Routines for dealing with the sorted snapshot functionality
+ */
+typedef struct zfs_node {
+ zfs_handle_t *zn_handle;
+ avl_node_t zn_avlnode;
+} zfs_node_t;
+
+static int
+zfs_sort_snaps(zfs_handle_t *zhp, void *data)
+{
+ avl_tree_t *avl = data;
+ zfs_node_t *node;
+ zfs_node_t search;
+
+ search.zn_handle = zhp;
+ node = avl_find(avl, &search, NULL);
+ if (node) {
+ /*
+ * If this snapshot was renamed while we were creating the
+ * AVL tree, it's possible that we already inserted it under
+ * its old name. Remove the old handle before adding the new
+ * one.
+ */
+ zfs_close(node->zn_handle);
+ avl_remove(avl, node);
+ free(node);
+ }
+
+ node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
+ node->zn_handle = zhp;
+ avl_add(avl, node);
+
+ return (0);
+}
+
+static int
+zfs_snapshot_compare(const void *larg, const void *rarg)
+{
+ zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
+ zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
+ uint64_t lcreate, rcreate;
+
+ /*
+ * Sort them according to creation time. We use the hidden
+ * CREATETXG property to get an absolute ordering of snapshots.
+ */
+ lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
+ rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
+
+ if (lcreate < rcreate)
+ return (-1);
+ else if (lcreate > rcreate)
+ return (+1);
+ else
+ return (0);
+}
+
+int
+zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
+{
+ int ret = 0;
+ zfs_node_t *node;
+ avl_tree_t avl;
+ void *cookie = NULL;
+
+ avl_create(&avl, zfs_snapshot_compare,
+ sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
+
+ ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
+
+ for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
+ ret |= callback(node->zn_handle, data);
+
+ while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
+ free(node);
+
+ avl_destroy(&avl);
+
+ return (ret);
+}
+
+typedef struct {
+ char *ssa_first;
+ char *ssa_last;
+ boolean_t ssa_seenfirst;
+ boolean_t ssa_seenlast;
+ zfs_iter_f ssa_func;
+ void *ssa_arg;
+} snapspec_arg_t;
+
+static int
+snapspec_cb(zfs_handle_t *zhp, void *arg) {
+ snapspec_arg_t *ssa = arg;
+ char *shortsnapname;
+ int err = 0;
+
+ if (ssa->ssa_seenlast)
+ return (0);
+ shortsnapname = zfs_strdup(zhp->zfs_hdl,
+ strchr(zfs_get_name(zhp), '@') + 1);
+
+ if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
+ ssa->ssa_seenfirst = B_TRUE;
+
+ if (ssa->ssa_seenfirst) {
+ err = ssa->ssa_func(zhp, ssa->ssa_arg);
+ } else {
+ zfs_close(zhp);
+ }
+
+ if (strcmp(shortsnapname, ssa->ssa_last) == 0)
+ ssa->ssa_seenlast = B_TRUE;
+ free(shortsnapname);
+
+ return (err);
+}
+
+/*
+ * spec is a string like "A,B%C,D"
+ *
+ * <snaps>, where <snaps> can be:
+ * <snap> (single snapshot)
+ * <snap>%<snap> (range of snapshots, inclusive)
+ * %<snap> (range of snapshots, starting with earliest)
+ * <snap>% (range of snapshots, ending with last)
+ * % (all snapshots)
+ * <snaps>[,...] (comma separated list of the above)
+ *
+ * If a snapshot can not be opened, continue trying to open the others, but
+ * return ENOENT at the end.
+ */
+int
+zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
+ zfs_iter_f func, void *arg)
+{
+ char buf[ZFS_MAXNAMELEN];
+ char *comma_separated, *cp;
+ int err = 0;
+ int ret = 0;
+
+ (void) strlcpy(buf, spec_orig, sizeof (buf));
+ cp = buf;
+
+ while ((comma_separated = strsep(&cp, ",")) != NULL) {
+ char *pct = strchr(comma_separated, '%');
+ if (pct != NULL) {
+ snapspec_arg_t ssa = { 0 };
+ ssa.ssa_func = func;
+ ssa.ssa_arg = arg;
+
+ if (pct == comma_separated)
+ ssa.ssa_seenfirst = B_TRUE;
+ else
+ ssa.ssa_first = comma_separated;
+ *pct = '\0';
+ ssa.ssa_last = pct + 1;
+
+ /*
+ * If there is a lastname specified, make sure it
+ * exists.
+ */
+ if (ssa.ssa_last[0] != '\0') {
+ char snapname[ZFS_MAXNAMELEN];
+ (void) snprintf(snapname, sizeof (snapname),
+ "%s@%s", zfs_get_name(fs_zhp),
+ ssa.ssa_last);
+ if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
+ snapname, ZFS_TYPE_SNAPSHOT)) {
+ ret = ENOENT;
+ continue;
+ }
+ }
+
+ err = zfs_iter_snapshots_sorted(fs_zhp,
+ snapspec_cb, &ssa);
+ if (ret == 0)
+ ret = err;
+ if (ret == 0 && (!ssa.ssa_seenfirst ||
+ (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
+ ret = ENOENT;
+ }
+ } else {
+ char snapname[ZFS_MAXNAMELEN];
+ zfs_handle_t *snap_zhp;
+ (void) snprintf(snapname, sizeof (snapname), "%s@%s",
+ zfs_get_name(fs_zhp), comma_separated);
+ snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
+ snapname);
+ if (snap_zhp == NULL) {
+ ret = ENOENT;
+ continue;
+ }
+ err = func(snap_zhp, arg);
+ if (ret == 0)
+ ret = err;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Iterate over all children, snapshots and filesystems
+ */
+int
+zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ int ret;
+
+ if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
+ return (ret);
+
+ return (zfs_iter_snapshots(zhp, func, data));
+}
+
+
+typedef struct iter_stack_frame {
+ struct iter_stack_frame *next;
+ zfs_handle_t *zhp;
+} iter_stack_frame_t;
+
+typedef struct iter_dependents_arg {
+ boolean_t first;
+ boolean_t allowrecursion;
+ iter_stack_frame_t *stack;
+ zfs_iter_f func;
+ void *data;
+} iter_dependents_arg_t;
+
+static int
+iter_dependents_cb(zfs_handle_t *zhp, void *arg)
+{
+ iter_dependents_arg_t *ida = arg;
+ int err;
+ boolean_t first = ida->first;
+ ida->first = B_FALSE;
+
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
+ err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
+ } else {
+ iter_stack_frame_t isf;
+ iter_stack_frame_t *f;
+
+ /*
+ * check if there is a cycle by seeing if this fs is already
+ * on the stack.
+ */
+ for (f = ida->stack; f != NULL; f = f->next) {
+ if (f->zhp->zfs_dmustats.dds_guid ==
+ zhp->zfs_dmustats.dds_guid) {
+ if (ida->allowrecursion) {
+ zfs_close(zhp);
+ return (0);
+ } else {
+ zfs_error_aux(zhp->zfs_hdl,
+ dgettext(TEXT_DOMAIN,
+ "recursive dependency at '%s'"),
+ zfs_get_name(zhp));
+ err = zfs_error(zhp->zfs_hdl,
+ EZFS_RECURSIVE,
+ dgettext(TEXT_DOMAIN,
+ "cannot determine dependent "
+ "datasets"));
+ zfs_close(zhp);
+ return (err);
+ }
+ }
+ }
+
+ isf.zhp = zhp;
+ isf.next = ida->stack;
+ ida->stack = &isf;
+ err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
+ if (err == 0)
+ err = zfs_iter_snapshots(zhp, iter_dependents_cb, ida);
+ ida->stack = isf.next;
+ }
+ if (!first && err == 0)
+ err = ida->func(zhp, ida->data);
+ return (err);
+}
+
+int
+zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
+ zfs_iter_f func, void *data)
+{
+ iter_dependents_arg_t ida;
+ ida.allowrecursion = allowrecursion;
+ ida.stack = NULL;
+ ida.func = func;
+ ida.data = data;
+ ida.first = B_TRUE;
+ return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c
index ba4b8199cc..8eb7e2ca15 100644
--- a/usr/src/lib/libzfs/common/libzfs_pool.c
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c
@@ -22,6 +22,8 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <ctype.h>
@@ -234,6 +236,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
case ZPOOL_PROP_ALTROOT:
case ZPOOL_PROP_CACHEFILE:
+ case ZPOOL_PROP_COMMENT:
if (zhp->zpool_props != NULL ||
zpool_get_all_props(zhp) == 0) {
(void) strlcpy(buf,
@@ -390,7 +393,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
zpool_prop_t prop;
char *strval;
uint64_t intval;
- char *slash;
+ char *slash, *check;
struct stat64 statbuf;
zpool_handle_t *zhp;
nvlist_t *nvroot;
@@ -549,6 +552,26 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
*slash = '/';
break;
+ case ZPOOL_PROP_COMMENT:
+ for (check = strval; *check != '\0'; check++) {
+ if (!isprint(*check)) {
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "comment may only have printable "
+ "characters"));
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ }
+ }
+ if (strlen(strval) > ZPROP_MAX_COMMENT) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "comment must not exceed %d characters"),
+ ZPROP_MAX_COMMENT);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
case ZPOOL_PROP_READONLY:
if (!flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -2975,6 +2998,26 @@ zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
}
/*
+ * Change the GUID for a pool.
+ */
+int
+zpool_reguid(zpool_handle_t *zhp)
+{
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+ zfs_cmd_t zc = { 0 };
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
+ return (0);
+
+ return (zpool_standard_error(hdl, errno, msg));
+}
+
+/*
* Convert from a devid string to a path.
*/
static char *
@@ -3707,8 +3750,7 @@ supported_dump_vdev_type(libzfs_handle_t *hdl, nvlist_t *config, char *errbuf)
uint_t children, c;
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_TYPE, &type) == 0);
- if (strcmp(type, VDEV_TYPE_RAIDZ) == 0 ||
- strcmp(type, VDEV_TYPE_FILE) == 0 ||
+ if (strcmp(type, VDEV_TYPE_FILE) == 0 ||
strcmp(type, VDEV_TYPE_LOG) == 0 ||
strcmp(type, VDEV_TYPE_HOLE) == 0 ||
strcmp(type, VDEV_TYPE_MISSING) == 0) {
diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
index 3093ab974d..224e46812a 100644
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <assert.h>
@@ -50,7 +51,7 @@
/* in libzfs_dataset.c */
extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
-static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t,
+static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t *,
int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *);
static const zio_cksum_t zero_cksum = { 0 };
@@ -771,88 +772,6 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
}
/*
- * Routines for dealing with the sorted snapshot functionality
- */
-typedef struct zfs_node {
- zfs_handle_t *zn_handle;
- avl_node_t zn_avlnode;
-} zfs_node_t;
-
-static int
-zfs_sort_snaps(zfs_handle_t *zhp, void *data)
-{
- avl_tree_t *avl = data;
- zfs_node_t *node;
- zfs_node_t search;
-
- search.zn_handle = zhp;
- node = avl_find(avl, &search, NULL);
- if (node) {
- /*
- * If this snapshot was renamed while we were creating the
- * AVL tree, it's possible that we already inserted it under
- * its old name. Remove the old handle before adding the new
- * one.
- */
- zfs_close(node->zn_handle);
- avl_remove(avl, node);
- free(node);
- }
-
- node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
- node->zn_handle = zhp;
- avl_add(avl, node);
-
- return (0);
-}
-
-static int
-zfs_snapshot_compare(const void *larg, const void *rarg)
-{
- zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
- zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
- uint64_t lcreate, rcreate;
-
- /*
- * Sort them according to creation time. We use the hidden
- * CREATETXG property to get an absolute ordering of snapshots.
- */
- lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
- rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
-
- if (lcreate < rcreate)
- return (-1);
- else if (lcreate > rcreate)
- return (+1);
- else
- return (0);
-}
-
-int
-zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
-{
- int ret = 0;
- zfs_node_t *node;
- avl_tree_t avl;
- void *cookie = NULL;
-
- avl_create(&avl, zfs_snapshot_compare,
- sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
-
- ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
-
- for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
- ret |= callback(node->zn_handle, data);
-
- while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
- free(node);
-
- avl_destroy(&avl);
-
- return (ret);
-}
-
-/*
* Routines specific to "zfs send"
*/
typedef struct send_dump_data {
@@ -862,7 +781,7 @@ typedef struct send_dump_data {
char prevsnap[ZFS_MAXNAMELEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose;
+ boolean_t verbose, dryrun, parsable;
int outfd;
boolean_t err;
nvlist_t *fss;
@@ -872,8 +791,69 @@ typedef struct send_dump_data {
nvlist_t *debugnv;
char holdtag[ZFS_MAXNAMELEN];
int cleanup_fd;
+ uint64_t size;
} send_dump_data_t;
+static int
+estimate_ioctl(zfs_handle_t *zhp, uint64_t fromsnap_obj,
+ boolean_t fromorigin, uint64_t *sizep)
+{
+ zfs_cmd_t zc = { 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ assert(fromsnap_obj == 0 || !fromorigin);
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ zc.zc_obj = fromorigin;
+ zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
+ zc.zc_fromobj = fromsnap_obj;
+ zc.zc_guid = 1; /* estimate flag */
+
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "warning: cannot estimate space for '%s'"), zhp->zfs_name);
+
+ switch (errno) {
+ case EXDEV:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "not an earlier snapshot from the same fs"));
+ return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
+
+ case ENOENT:
+ if (zfs_dataset_exists(hdl, zc.zc_name,
+ ZFS_TYPE_SNAPSHOT)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "incremental source (@%s) does not exist"),
+ zc.zc_value);
+ }
+ return (zfs_error(hdl, EZFS_NOENT, errbuf));
+
+ case EDQUOT:
+ case EFBIG:
+ case EIO:
+ case ENOLINK:
+ case ENOSPC:
+ case ENOSTR:
+ case ENXIO:
+ case EPIPE:
+ case ERANGE:
+ case EFAULT:
+ case EROFS:
+ zfs_error_aux(hdl, strerror(errno));
+ return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
+
+ default:
+ return (zfs_standard_error(hdl, errno, errbuf));
+ }
+ }
+
+ *sizep = zc.zc_objset_type;
+
+ return (0);
+}
+
/*
* Dumps a backup of the given snapshot (incremental from fromsnap if it's not
* NULL) to the file descriptor specified by outfd.
@@ -901,7 +881,7 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
"fromsnap", fromsnap));
}
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SEND, &zc) != 0) {
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"warning: cannot send '%s'"), zhp->zfs_name);
@@ -914,7 +894,6 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
nvlist_free(thisdbg);
switch (errno) {
-
case EXDEV:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"not an earlier snapshot from the same fs"));
@@ -964,6 +943,9 @@ hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ if (sdd->dryrun)
+ return (0);
+
/*
* zfs_send() only opens a cleanup_fd for sends that need it,
* e.g. replication and doall.
@@ -997,7 +979,7 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
send_dump_data_t *sdd = arg;
char *thissnap;
int err;
- boolean_t isfromsnap, istosnap;
+ boolean_t isfromsnap, istosnap, fromorigin;
boolean_t exclude = B_FALSE;
thissnap = strchr(zhp->zfs_name, '@') + 1;
@@ -1074,15 +1056,47 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
return (err);
}
- /* send it */
+ fromorigin = sdd->prevsnap[0] == '\0' &&
+ (sdd->fromorigin || sdd->replicate);
+
if (sdd->verbose) {
- (void) fprintf(stderr, "sending from @%s to %s\n",
- sdd->prevsnap, zhp->zfs_name);
+ uint64_t size;
+ err = estimate_ioctl(zhp, sdd->prevsnap_obj,
+ fromorigin, &size);
+
+ if (sdd->parsable) {
+ if (sdd->prevsnap[0] != '\0') {
+ (void) fprintf(stderr, "incremental\t%s\t%s",
+ sdd->prevsnap, zhp->zfs_name);
+ } else {
+ (void) fprintf(stderr, "full\t%s",
+ zhp->zfs_name);
+ }
+ } else {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "send from @%s to %s"),
+ sdd->prevsnap, zhp->zfs_name);
+ }
+ if (err == 0) {
+ if (sdd->parsable) {
+ (void) fprintf(stderr, "\t%llu\n",
+ (longlong_t)size);
+ } else {
+ char buf[16];
+ zfs_nicenum(size, buf, sizeof (buf));
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ " estimated size is %s\n"), buf);
+ }
+ sdd->size += size;
+ } else {
+ (void) fprintf(stderr, "\n");
+ }
}
- err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
- sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
- sdd->outfd, sdd->debugnv);
+ if (!sdd->dryrun) {
+ err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
+ fromorigin, sdd->outfd, sdd->debugnv);
+ }
(void) strcpy(sdd->prevsnap, thissnap);
sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
@@ -1101,8 +1115,8 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
zhp->zfs_name, sdd->tosnap);
if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- (void) fprintf(stderr, "WARNING: "
- "could not send %s@%s: does not exist\n",
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "WARNING: could not send %s@%s: does not exist\n"),
zhp->zfs_name, sdd->tosnap);
sdd->err = B_TRUE;
return (0);
@@ -1131,23 +1145,24 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
if (!sdd->seenfrom) {
- (void) fprintf(stderr,
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) does not exist\n",
+ "incremental source (%s@%s) does not exist\n"),
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
sdd->err = B_TRUE;
} else if (!sdd->seento) {
if (sdd->fromsnap) {
- (void) fprintf(stderr,
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"WARNING: could not send %s@%s:\n"
"incremental source (%s@%s) "
- "is not earlier than it\n",
+ "is not earlier than it\n"),
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
} else {
- (void) fprintf(stderr, "WARNING: "
- "could not send %s@%s: does not exist\n",
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "WARNING: "
+ "could not send %s@%s: does not exist\n"),
zhp->zfs_name, sdd->tosnap);
}
sdd->err = B_TRUE;
@@ -1193,11 +1208,12 @@ again:
needagain = progress = B_FALSE;
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist;
+ nvlist_t *fslist, *parent_nv;
char *fsname;
zfs_handle_t *zhp;
int err;
uint64_t origin_guid = 0;
+ uint64_t parent_guid = 0;
VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
if (nvlist_lookup_boolean(fslist, "sent") == 0)
@@ -1205,13 +1221,23 @@ again:
VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
(void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
+ (void) nvlist_lookup_uint64(fslist, "parentfromsnap",
+ &parent_guid);
+
+ if (parent_guid != 0) {
+ parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
+ if (!nvlist_exists(parent_nv, "sent")) {
+ /* parent has not been sent; skip this one */
+ needagain = B_TRUE;
+ continue;
+ }
+ }
if (origin_guid != 0) {
nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
origin_guid, NULL);
if (origin_nv != NULL &&
- nvlist_lookup_boolean(origin_nv,
- "sent") == ENOENT) {
+ !nvlist_exists(origin_nv, "sent")) {
/*
* origin has not been sent yet;
* skip this clone.
@@ -1235,6 +1261,16 @@ again:
assert(progress);
goto again;
}
+
+ /* clean out the sent flags in case we reuse this fss */
+ for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
+ fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
+ nvlist_t *fslist;
+
+ VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
+ (void) nvlist_remove_all(fslist, "sent");
+ }
+
return (0);
}
@@ -1256,12 +1292,12 @@ again:
*/
int
zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
+ sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
void *cb_arg, nvlist_t **debugnvp)
{
char errbuf[1024];
send_dump_data_t sdd = { 0 };
- int err;
+ int err = 0;
nvlist_t *fss = NULL;
avl_tree_t *fsavl = NULL;
static uint64_t holdseq;
@@ -1289,12 +1325,12 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (zfs_spa_version(zhp, &spa_version) == 0 &&
+ if (!flags->dryrun && zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS &&
- (flags.doall || flags.replicate))
+ (flags->doall || flags->replicate))
holdsnaps = B_TRUE;
- if (flags.dedup) {
+ if (flags->dedup && !flags->dryrun) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS);
if (err = pipe(pipefd)) {
@@ -1314,13 +1350,13 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (flags.replicate || flags.doall || flags.props) {
+ if (flags->replicate || flags->doall || flags->props) {
dmu_replay_record_t drr = { 0 };
char *packbuf = NULL;
size_t buflen = 0;
zio_cksum_t zc = { 0 };
- if (flags.replicate || flags.props) {
+ if (flags->replicate || flags->props) {
nvlist_t *hdrnv;
VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
@@ -1329,13 +1365,13 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
"fromsnap", fromsnap));
}
VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
- if (!flags.replicate) {
+ if (!flags->replicate) {
VERIFY(0 == nvlist_add_boolean(hdrnv,
"not_recursive"));
}
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
- fromsnap, tosnap, flags.replicate, &fss, &fsavl);
+ fromsnap, tosnap, flags->replicate, &fss, &fsavl);
if (err)
goto err_out;
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
@@ -1352,33 +1388,34 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- /* write first begin record */
- drr.drr_type = DRR_BEGIN;
- drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
- DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.drr_versioninfo,
- DMU_COMPOUNDSTREAM);
- DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.drr_versioninfo,
- featureflags);
- (void) snprintf(drr.drr_u.drr_begin.drr_toname,
- sizeof (drr.drr_u.drr_begin.drr_toname),
- "%s@%s", zhp->zfs_name, tosnap);
- drr.drr_payloadlen = buflen;
- err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
-
- /* write header nvlist */
- if (err != -1 && packbuf != NULL) {
- err = cksum_and_write(packbuf, buflen, &zc, outfd);
- }
- free(packbuf);
- if (err == -1) {
- fsavl_destroy(fsavl);
- nvlist_free(fss);
- err = errno;
- goto stderr_out;
- }
+ if (!flags->dryrun) {
+ /* write first begin record */
+ drr.drr_type = DRR_BEGIN;
+ drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
+ DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
+ drr_versioninfo, DMU_COMPOUNDSTREAM);
+ DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
+ drr_versioninfo, featureflags);
+ (void) snprintf(drr.drr_u.drr_begin.drr_toname,
+ sizeof (drr.drr_u.drr_begin.drr_toname),
+ "%s@%s", zhp->zfs_name, tosnap);
+ drr.drr_payloadlen = buflen;
+ err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
+
+ /* write header nvlist */
+ if (err != -1 && packbuf != NULL) {
+ err = cksum_and_write(packbuf, buflen, &zc,
+ outfd);
+ }
+ free(packbuf);
+ if (err == -1) {
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+ err = errno;
+ goto stderr_out;
+ }
- /* write end record */
- if (err != -1) {
+ /* write end record */
bzero(&drr, sizeof (drr));
drr.drr_type = DRR_END;
drr.drr_u.drr_end.drr_checksum = zc;
@@ -1389,22 +1426,26 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
err = errno;
goto stderr_out;
}
+
+ err = 0;
}
}
/* dump each stream */
sdd.fromsnap = fromsnap;
sdd.tosnap = tosnap;
- if (flags.dedup)
+ if (flags->dedup)
sdd.outfd = pipefd[0];
else
sdd.outfd = outfd;
- sdd.replicate = flags.replicate;
- sdd.doall = flags.doall;
- sdd.fromorigin = flags.fromorigin;
+ sdd.replicate = flags->replicate;
+ sdd.doall = flags->doall;
+ sdd.fromorigin = flags->fromorigin;
sdd.fss = fss;
sdd.fsavl = fsavl;
- sdd.verbose = flags.verbose;
+ sdd.verbose = flags->verbose;
+ sdd.parsable = flags->parsable;
+ sdd.dryrun = flags->dryrun;
sdd.filter_cb = filter_func;
sdd.filter_cb_arg = cb_arg;
if (debugnvp)
@@ -1421,11 +1462,31 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
} else {
sdd.cleanup_fd = -1;
}
+ if (flags->verbose) {
+ /*
+ * Do a verbose no-op dry run to get all the verbose output
+ * before generating any data. Then do a non-verbose real
+ * run to generate the streams.
+ */
+ sdd.dryrun = B_TRUE;
+ err = dump_filesystems(zhp, &sdd);
+ sdd.dryrun = flags->dryrun;
+ sdd.verbose = B_FALSE;
+ if (flags->parsable) {
+ (void) fprintf(stderr, "size\t%llu\n",
+ (longlong_t)sdd.size);
+ } else {
+ char buf[16];
+ zfs_nicenum(sdd.size, buf, sizeof (buf));
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "total estimated size is %s\n"), buf);
+ }
+ }
err = dump_filesystems(zhp, &sdd);
fsavl_destroy(fsavl);
nvlist_free(fss);
- if (flags.dedup) {
+ if (flags->dedup) {
(void) close(pipefd[0]);
(void) pthread_join(tid, NULL);
}
@@ -1435,7 +1496,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.cleanup_fd = -1;
}
- if (flags.replicate || flags.doall || flags.props) {
+ if (!flags->dryrun && (flags->replicate || flags->doall ||
+ flags->props)) {
/*
* write final end record. NB: want to do this even if
* there was some error, because it might not be totally
@@ -1456,7 +1518,7 @@ stderr_out:
err_out:
if (sdd.cleanup_fd != -1)
VERIFY(0 == close(sdd.cleanup_fd));
- if (flags.dedup) {
+ if (flags->dedup) {
(void) pthread_cancel(tid);
(void) pthread_join(tid, NULL);
(void) close(pipefd[0]);
@@ -1527,7 +1589,7 @@ recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
static int
recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
- int baselen, char *newname, recvflags_t flags)
+ int baselen, char *newname, recvflags_t *flags)
{
static int seq;
zfs_cmd_t zc = { 0 };
@@ -1539,7 +1601,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags.force ? MS_FORCE : 0);
+ flags->force ? MS_FORCE : 0);
zfs_close(zhp);
if (clp == NULL)
return (-1);
@@ -1555,7 +1617,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("attempting rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
@@ -1574,19 +1636,19 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
"recv-%u-%u", getpid(), seq);
(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("failed - trying rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
if (err == 0)
changelist_rename(clp, name, newname);
- if (err && flags.verbose) {
+ if (err && flags->verbose) {
(void) printf("failed (%u) - "
"will try again on next pass\n", errno);
}
err = EAGAIN;
- } else if (flags.verbose) {
+ } else if (flags->verbose) {
if (err == 0)
(void) printf("success\n");
else
@@ -1601,7 +1663,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
static int
recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
- char *newname, recvflags_t flags)
+ char *newname, recvflags_t *flags)
{
zfs_cmd_t zc = { 0 };
int err = 0;
@@ -1614,7 +1676,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags.force ? MS_FORCE : 0);
+ flags->force ? MS_FORCE : 0);
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS)
@@ -1630,11 +1692,11 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
zc.zc_defer_destroy = defer;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("attempting destroy %s\n", zc.zc_name);
err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
if (err == 0) {
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("success\n");
changelist_remove(clp, zc.zc_name);
}
@@ -1657,6 +1719,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
typedef struct guid_to_name_data {
uint64_t guid;
char *name;
+ char *skip;
} guid_to_name_data_t;
static int
@@ -1665,21 +1728,35 @@ guid_to_name_cb(zfs_handle_t *zhp, void *arg)
guid_to_name_data_t *gtnd = arg;
int err;
+ if (gtnd->skip != NULL &&
+ strcmp(zhp->zfs_name, gtnd->skip) == 0) {
+ return (0);
+ }
+
if (zhp->zfs_dmustats.dds_guid == gtnd->guid) {
(void) strcpy(gtnd->name, zhp->zfs_name);
zfs_close(zhp);
return (EEXIST);
}
+
err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
zfs_close(zhp);
return (err);
}
+/*
+ * Attempt to find the local dataset associated with this guid. In the case of
+ * multiple matches, we attempt to find the "best" match by searching
+ * progressively larger portions of the hierarchy. This allows one to send a
+ * tree of datasets individually and guarantee that we will find the source
+ * guid within that hierarchy, even if there are multiple matches elsewhere.
+ */
static int
guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
char *name)
{
/* exhaustive search all local snapshots */
+ char pname[ZFS_MAXNAMELEN];
guid_to_name_data_t gtnd;
int err = 0;
zfs_handle_t *zhp;
@@ -1687,35 +1764,42 @@ guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
gtnd.guid = guid;
gtnd.name = name;
+ gtnd.skip = NULL;
- if (strchr(parent, '@') == NULL) {
- zhp = make_dataset_handle(hdl, parent);
- if (zhp != NULL) {
- err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
- zfs_close(zhp);
- if (err == EEXIST)
- return (0);
- }
- }
+ (void) strlcpy(pname, parent, sizeof (pname));
- cp = strchr(parent, '/');
- if (cp)
+ /*
+ * Search progressively larger portions of the hierarchy. This will
+ * select the "most local" version of the origin snapshot in the case
+ * that there are multiple matching snapshots in the system.
+ */
+ while ((cp = strrchr(pname, '/')) != NULL) {
+
+ /* Chop off the last component and open the parent */
*cp = '\0';
- zhp = make_dataset_handle(hdl, parent);
- if (cp)
- *cp = '/';
+ zhp = make_dataset_handle(hdl, pname);
+
+ if (zhp == NULL)
+ continue;
- if (zhp) {
err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
zfs_close(zhp);
- }
+ if (err == EEXIST)
+ return (0);
- return (err == EEXIST ? 0 : ENOENT);
+ /*
+ * Remember the dataset that we already searched, so we
+ * skip it next time through.
+ */
+ gtnd.skip = pname;
+ }
+ return (ENOENT);
}
/*
- * Return true if dataset guid1 is created before guid2.
+ * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
+ * guid1 is after guid2.
*/
static int
created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
@@ -1725,7 +1809,8 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
char *fsname, *snapname;
char buf[ZFS_MAXNAMELEN];
int rv;
- zfs_node_t zn1, zn2;
+ zfs_handle_t *guid1hdl, *guid2hdl;
+ uint64_t create1, create2;
if (guid2 == 0)
return (0);
@@ -1735,30 +1820,38 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
nvfs = fsavl_find(avl, guid1, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- zn1.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (zn1.zn_handle == NULL)
+ guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (guid1hdl == NULL)
return (-1);
nvfs = fsavl_find(avl, guid2, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- zn2.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (zn2.zn_handle == NULL) {
- zfs_close(zn2.zn_handle);
+ guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (guid2hdl == NULL) {
+ zfs_close(guid1hdl);
return (-1);
}
- rv = (zfs_snapshot_compare(&zn1, &zn2) == -1);
+ create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
+ create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
- zfs_close(zn1.zn_handle);
- zfs_close(zn2.zn_handle);
+ if (create1 < create2)
+ rv = -1;
+ else if (create1 > create2)
+ rv = +1;
+ else
+ rv = 0;
+
+ zfs_close(guid1hdl);
+ zfs_close(guid2hdl);
return (rv);
}
static int
recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
- recvflags_t flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
+ recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
nvlist_t *renamed)
{
nvlist_t *local_nv;
@@ -1775,7 +1868,7 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
ENOENT);
- if (flags.dryrun)
+ if (flags->dryrun)
return (0);
again:
@@ -1835,7 +1928,7 @@ again:
nvlist_t *origin_nvfs;
char *origin_fsname;
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("promoting %s\n", fsname);
origin_nvfs = fsavl_find(local_avl, originguid,
@@ -1883,7 +1976,7 @@ again:
if (found == NULL) {
char name[ZFS_MAXNAMELEN];
- if (!flags.force)
+ if (!flags->force)
continue;
(void) snprintf(name, sizeof (name), "%s@%s",
@@ -1941,7 +2034,7 @@ again:
/* check for delete */
if (stream_nvfs == NULL) {
- if (!flags.force)
+ if (!flags->force)
continue;
error = recv_destroy(hdl, fsname, strlen(tofs)+1,
@@ -1954,7 +2047,7 @@ again:
}
if (fromguid == 0) {
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("local fs %s does not have "
"fromsnap (%s in stream); must have "
"been deleted locally; ignoring\n",
@@ -1979,7 +2072,7 @@ again:
if ((stream_parent_fromsnap_guid != 0 &&
parent_fromsnap_guid != 0 &&
stream_parent_fromsnap_guid != parent_fromsnap_guid) ||
- ((flags.isprefix || strcmp(tofs, fsname) != 0) &&
+ ((flags->isprefix || strcmp(tofs, fsname) != 0) &&
(s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) {
nvlist_t *parent;
char tryname[ZFS_MAXNAMELEN];
@@ -2002,7 +2095,7 @@ again:
"%s%s", pname, strrchr(stream_fsname, '/'));
} else {
tryname[0] = '\0';
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("local fs %s new parent "
"not found\n", fsname);
}
@@ -2030,7 +2123,7 @@ again:
if (needagain && progress) {
/* do another pass to fix up temporary names */
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("another pass:\n");
goto again;
}
@@ -2040,7 +2133,7 @@ again:
static int
zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
- recvflags_t flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
+ recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
nvlist_t *stream_nv = NULL;
@@ -2069,7 +2162,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
*/
if (drr->drr_payloadlen != 0) {
error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen,
- &stream_nv, flags.byteswap, zc);
+ &stream_nv, flags->byteswap, zc);
if (error) {
error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
goto out;
@@ -2090,9 +2183,9 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
* Read in the end record and verify checksum.
*/
if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre),
- flags.byteswap, NULL)))
+ flags->byteswap, NULL)))
goto out;
- if (flags.byteswap) {
+ if (flags->byteswap) {
drre.drr_type = BSWAP_32(drre.drr_type);
drre.drr_u.drr_end.drr_checksum.zc_word[0] =
BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]);
@@ -2133,11 +2226,11 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
nvpair_t *pair = NULL;
(void) strlcpy(tofs, destname, ZFS_MAXNAMELEN);
- if (flags.isprefix) {
+ if (flags->isprefix) {
struct drr_begin *drrb = &drr->drr_u.drr_begin;
int i;
- if (flags.istail) {
+ if (flags->istail) {
cp = strrchr(drrb->drr_toname, '/');
if (cp == NULL) {
(void) strlcat(tofs, "/",
@@ -2155,7 +2248,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
*strchr(tofs, '@') = '\0';
}
- if (recursive && !flags.dryrun && !flags.nomount) {
+ if (recursive && !flags->dryrun && !flags->nomount) {
VERIFY(0 == nvlist_alloc(&renamed,
NV_UNIQUE_NAME, 0));
}
@@ -2329,7 +2422,7 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
*/
static int
zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
- recvflags_t flags, dmu_replay_record_t *drr,
+ recvflags_t *flags, dmu_replay_record_t *drr,
dmu_replay_record_t *drr_noswap, const char *sendfs,
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
uint64_t *action_handlep)
@@ -2371,7 +2464,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
if (err)
VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
- if (flags.canmountoff) {
+ if (flags->canmountoff) {
VERIFY(0 == nvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
}
@@ -2398,7 +2491,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* If they specified a snapshot, chop the entire name stored in
* the stream.
*/
- if (flags.istail) {
+ if (flags->istail) {
/*
* A filesystem was specified with -e. We want to tack on only
* the tail of the sent snapshot path.
@@ -2424,7 +2517,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
} else {
chopprefix = drrb->drr_toname + (chopprefix - sendfs);
}
- } else if (flags.isprefix) {
+ } else if (flags->isprefix) {
/*
* A filesystem was specified with -d. We want to tack on
* everything but the first element of the sent snapshot path
@@ -2478,7 +2571,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* Determine the name of the origin snapshot, store in zc_string.
*/
if (drrb->drr_flags & DRR_FLAG_CLONE) {
- if (guid_to_name(hdl, tosnap,
+ if (guid_to_name(hdl, zc.zc_value,
drrb->drr_fromguid, zc.zc_string) != 0) {
zcmd_free_nvlists(&zc);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -2486,7 +2579,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zc.zc_value);
return (zfs_error(hdl, EZFS_NOENT, errbuf));
}
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("found clone origin %s\n", zc.zc_string);
}
@@ -2509,7 +2602,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
!zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
char suffix[ZFS_MAXNAMELEN];
(void) strcpy(suffix, strrchr(zc.zc_value, '/'));
- if (guid_to_name(hdl, tosnap, parent_snapguid,
+ if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
zc.zc_value) == 0) {
*strchr(zc.zc_value, '@') = '\0';
(void) strcat(zc.zc_value, suffix);
@@ -2531,12 +2624,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* topmost path in the stream, then if the fs does not exist we
* should look no further.
*/
- if ((flags.isprefix || (*(chopprefix = drrb->drr_toname +
+ if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
!zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
char snap[ZFS_MAXNAMELEN];
(void) strcpy(snap, strchr(zc.zc_value, '@'));
- if (guid_to_name(hdl, tosnap, drrb->drr_fromguid,
+ if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
zc.zc_value) == 0) {
*strchr(zc.zc_value, '@') = '\0';
(void) strcat(zc.zc_value, snap);
@@ -2558,7 +2651,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* snapshots).
*/
if (stream_wantsnewfs) {
- if (!flags.force) {
+ if (!flags->force) {
zcmd_free_nvlists(&zc);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination '%s' exists\n"
@@ -2594,7 +2687,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
return (zfs_error(hdl, EZFS_EXISTS, errbuf));
}
- if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
+ if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
stream_wantsnewfs) {
/* We can't do online recv in this case */
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
@@ -2633,7 +2726,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
*/
*cp = '\0';
- if (flags.isprefix && !flags.istail && !flags.dryrun &&
+ if (flags->isprefix && !flags->istail && !flags->dryrun &&
create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
zcmd_free_nvlists(&zc);
return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
@@ -2644,18 +2737,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zc.zc_begin_record = drr_noswap->drr_u.drr_begin;
zc.zc_cookie = infd;
- zc.zc_guid = flags.force;
- if (flags.verbose) {
+ zc.zc_guid = flags->force;
+ if (flags->verbose) {
(void) printf("%s %s stream of %s into %s\n",
- flags.dryrun ? "would receive" : "receiving",
+ flags->dryrun ? "would receive" : "receiving",
drrb->drr_fromguid ? "incremental" : "full",
drrb->drr_toname, zc.zc_value);
(void) fflush(stdout);
}
- if (flags.dryrun) {
+ if (flags->dryrun) {
zcmd_free_nvlists(&zc);
- return (recv_skip(hdl, infd, flags.byteswap));
+ return (recv_skip(hdl, infd, flags->byteswap));
}
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
@@ -2736,12 +2829,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
nvlist_free(local_nv);
if (fs != NULL) {
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("snap %s already exists; "
"ignoring\n", zc.zc_value);
}
err = ioctl_err = recv_skip(hdl, infd,
- flags.byteswap);
+ flags->byteswap);
}
}
*cp = '@';
@@ -2793,7 +2886,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
case EDQUOT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination %s space quota exceeded"), zc.zc_name);
- (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
+ (void) zfs_error(hdl, EZFS_NOSPC, errbuf);
break;
default:
(void) zfs_standard_error(hdl, ioctl_errno, errbuf);
@@ -2851,7 +2944,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
*action_handlep = zc.zc_action_handle;
- if (flags.verbose) {
+ if (flags->verbose) {
char buf1[64];
char buf2[64];
uint64_t bytes = zc.zc_cookie;
@@ -2869,7 +2962,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
}
static int
-zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
int infd, const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl,
char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
@@ -2884,7 +2977,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot receive"));
- if (flags.isprefix &&
+ if (flags->isprefix &&
!zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
"(%s) does not exist"), tosnap);
@@ -2904,7 +2997,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
/* the kernel needs the non-byteswapped begin record */
drr_noswap = drr;
- flags.byteswap = B_FALSE;
+ flags->byteswap = B_FALSE;
if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
/*
* We computed the checksum in the wrong byteorder in
@@ -2912,7 +3005,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
*/
bzero(&zcksum, sizeof (zio_cksum_t));
fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum);
- flags.byteswap = B_TRUE;
+ flags->byteswap = B_TRUE;
drr.drr_type = BSWAP_32(drr.drr_type);
drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
@@ -2980,7 +3073,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
* (-1 will override -2).
*/
int
-zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
int infd, avl_tree_t *stream_avl)
{
char *top_zfs = NULL;
@@ -2996,7 +3089,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
VERIFY(0 == close(cleanup_fd));
- if (err == 0 && !flags.nomount && top_zfs) {
+ if (err == 0 && !flags->nomount && top_zfs) {
zfs_handle_t *zhp;
prop_changelist_t *clp;
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 01b7c8732e..6754bb98d0 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
@@ -344,6 +345,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
switch (error) {
case ENXIO:
case ENODEV:
+ case EPIPE:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
@@ -1281,7 +1283,8 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
* dataset property,
*/
if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
- (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
+ (!zfs_prop_user(propname) && !zfs_prop_userquota(propname) &&
+ !zfs_prop_written(propname)))) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
return (zfs_error(hdl, EZFS_BADPROP,
diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers
index b435b58e05..e4a45d3ecb 100644
--- a/usr/src/lib/libzfs/common/mapfile-vers
+++ b/usr/src/lib/libzfs/common/mapfile-vers
@@ -19,7 +19,8 @@
# CDDL HEADER END
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2010 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+# Copyright (c) 2011 by Delphix. All rights reserved.
#
# MAPFILE HEADER START
#
@@ -68,13 +69,16 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_deleg_share_nfs;
zfs_destroy;
zfs_destroy_snaps;
+ zfs_destroy_snaps_nvl;
zfs_expand_proplist;
zfs_get_handle;
zfs_get_holds;
zfs_get_name;
zfs_get_pool_handle;
+ zfs_get_snapused_int;
zfs_get_user_props;
zfs_get_type;
+ zfs_handle_dup;
zfs_history_event_names;
zfs_hold;
zfs_is_mounted;
@@ -87,6 +91,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_iter_root;
zfs_iter_snapshots;
zfs_iter_snapshots_sorted;
+ zfs_iter_snapspec;
zfs_mount;
zfs_name_to_prop;
zfs_name_valid;
@@ -106,6 +111,8 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_prop_get_table;
zfs_prop_get_userquota_int;
zfs_prop_get_userquota;
+ zfs_prop_get_written_int;
+ zfs_prop_get_written;
zfs_prop_inherit;
zfs_prop_inheritable;
zfs_prop_init;
@@ -118,6 +125,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_prop_userquota;
zfs_prop_valid_for_type;
zfs_prop_values;
+ zfs_prop_written;
zfs_prune_proplist;
zfs_receive;
zfs_refresh_properties;
@@ -199,6 +207,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zpool_prop_values;
zpool_read_label;
zpool_refresh_stats;
+ zpool_reguid;
zpool_scan;
zpool_search_import;
zpool_set_history_str;
diff --git a/usr/src/lib/libzonecfg/common/getzoneent.c b/usr/src/lib/libzonecfg/common/getzoneent.c
index 76cc918553..76664fcc92 100644
--- a/usr/src/lib/libzonecfg/common/getzoneent.c
+++ b/usr/src/lib/libzonecfg/common/getzoneent.c
@@ -403,14 +403,6 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
*/
if (ze->zone_state >= 0) {
zone_state = zone_state_str(ze->zone_state);
-
- /*
- * If the caller is uninstalling this zone,
- * then wipe out the uuid. The zone's contents
- * are no longer known.
- */
- if (ze->zone_state < ZONE_STATE_INSTALLED)
- zone_uuid = "";
}
/* If a new name is supplied, use it. */
diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c
index f5ac4975d8..97fd1f52e2 100644
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c
@@ -4768,7 +4768,7 @@ get_pool_sched_class(char *poolname, char *class, int clsize)
pool_conf_t *poolconf;
pool_t *pool;
pool_elem_t *pe;
- pool_value_t *pv = pool_value_alloc();
+ pool_value_t *pv;
const char *sched_str;
if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
@@ -4789,15 +4789,23 @@ get_pool_sched_class(char *poolname, char *class, int clsize)
return (Z_NO_POOL);
}
+ if ((pv = pool_value_alloc()) == NULL) {
+ (void) pool_conf_close(poolconf);
+ pool_conf_free(poolconf);
+ return (Z_NO_POOL);
+ }
+
pe = pool_to_elem(poolconf, pool);
if (pool_get_property(poolconf, pe, "pool.scheduler", pv) !=
POC_STRING) {
(void) pool_conf_close(poolconf);
+ pool_value_free(pv);
pool_conf_free(poolconf);
return (Z_NO_ENTRY);
}
(void) pool_value_get_string(pv, &sched_str);
(void) pool_conf_close(poolconf);
+ pool_value_free(pv);
pool_conf_free(poolconf);
if (strlcpy(class, sched_str, clsize) >= clsize)
return (Z_TOO_BIG);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c
index 5f62c02e51..fd8e091147 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c
@@ -17,9 +17,9 @@
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
- */
-/*
+ *
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -739,9 +739,6 @@ smb_shr_exists(char *sharename)
* Precedence is none is checked first followed by ro then rw if
* needed. If x is wildcard (< 0) then check to see if the other
* values are a match. If a match, that wins.
- *
- * ipv6 is wide open (returns SMB_SHRF_ACC_OPEN) for now until the underlying
- * functions support ipv6.
*/
uint32_t
smb_shr_hostaccess(smb_inaddr_t *ipaddr, char *none_list, char *ro_list,
@@ -753,10 +750,6 @@ smb_shr_hostaccess(smb_inaddr_t *ipaddr, char *none_list, char *ro_list,
int rw = 0;
if (!smb_inet_iszero(ipaddr)) {
-
- if (ipaddr->a_family == AF_INET6)
- return (SMB_SHRF_ACC_OPEN);
-
if ((flag & SMB_SHRF_ACC_NONE) != 0)
none = smb_chk_hostaccess(ipaddr, none_list);
if ((flag & SMB_SHRF_ACC_RO) != 0)
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c b/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c
index ea51a7e369..8435a20c32 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -1033,14 +1034,25 @@ smb_lgrp_err_to_ntstatus(uint32_t lgrp_err)
* smb_lgrp_chkmember
*
* Determines valid account types for being member of
- * a local group.
- *
- * Currently, we just support users as valid members.
+ * a local group. We really have no business trying to
+ * keep track of the "type" of SIDs in a group, so just
+ * validate that the SID type is a known enum value.
*/
static boolean_t
smb_lgrp_chkmember(uint16_t sid_type)
{
- return (sid_type == SidTypeUser);
+ switch (sid_type) {
+ case SidTypeNull:
+ case SidTypeUser:
+ case SidTypeGroup:
+ case SidTypeAlias:
+ case SidTypeWellKnownGroup:
+ case SidTypeDeletedAccount:
+ case SidTypeInvalid:
+ case SidTypeUnknown:
+ return (B_TRUE);
+ }
+ return (B_FALSE);
}
/*
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_sam.c b/usr/src/lib/smbsrv/libsmb/common/smb_sam.c
index 21be70116b..7016f5a878 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_sam.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_sam.c
@@ -19,6 +19,7 @@
* CDDL HEADER END
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -299,24 +300,45 @@ smb_sam_usr_cnt(void)
}
/*
- * Returns a list of local groups which the given user is
- * their member. A pointer to an array of smb_ids_t
- * structure is returned which must be freed by caller.
+ * Updates a list of groups in which the given user is a member
+ * by adding any local (SAM) groups.
+ *
+ * We are a member of local groups where the local group
+ * contains either the user's primary SID, or any of their
+ * other SIDs such as from domain groups, SID history, etc.
+ * We can have indirect membership via domain groups.
*/
uint32_t
smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids)
{
- smb_id_t *ids;
+ smb_ids_t new_gids;
+ smb_id_t *ids, *new_ids;
smb_giter_t gi;
smb_group_t lgrp;
- int total_cnt, gcnt;
+ int i, gcnt, total_cnt;
+ uint32_t ret;
+ boolean_t member;
+ /*
+ * First pass: count groups to be added (gcnt)
+ */
gcnt = 0;
if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
return (NT_STATUS_INTERNAL_ERROR);
while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
+ member = B_FALSE;
if (smb_lgrp_is_member(&lgrp, user_sid))
+ member = B_TRUE;
+ else for (i = 0, ids = gids->i_ids;
+ i < gids->i_cnt; i++, ids++) {
+ if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
+ member = B_TRUE;
+ break;
+ }
+ }
+ /* Careful: only count lgrp once */
+ if (member)
gcnt++;
smb_lgrp_free(&lgrp);
}
@@ -325,35 +347,86 @@ smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids)
if (gcnt == 0)
return (NT_STATUS_SUCCESS);
- total_cnt = gids->i_cnt + gcnt;
- gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
- if (gids->i_ids == NULL)
- return (NT_STATUS_NO_MEMORY);
-
+ /*
+ * Second pass: add to groups list.
+ * Do not modify gcnt after here.
+ */
if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
return (NT_STATUS_INTERNAL_ERROR);
- ids = gids->i_ids + gids->i_cnt;
+ /*
+ * Expand the list (copy to a new, larger one)
+ * Note: were're copying pointers from the old
+ * array to the new (larger) array, and then
+ * adding new pointers after what we copied.
+ */
+ ret = 0;
+ new_gids.i_cnt = gids->i_cnt;
+ total_cnt = gids->i_cnt + gcnt;
+ new_gids.i_ids = malloc(total_cnt * sizeof (smb_id_t));
+ if (new_gids.i_ids == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ (void) memcpy(new_gids.i_ids, gids->i_ids,
+ gids->i_cnt * sizeof (smb_id_t));
+ new_ids = new_gids.i_ids + gids->i_cnt;
+ (void) memset(new_ids, 0, gcnt * sizeof (smb_id_t));
+
+ /*
+ * Add group SIDs starting at the end of the
+ * previous list. (new_ids)
+ */
while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
- if (gcnt == 0) {
- smb_lgrp_free(&lgrp);
- break;
+ member = B_FALSE;
+ if (smb_lgrp_is_member(&lgrp, user_sid))
+ member = B_TRUE;
+ else for (i = 0, ids = gids->i_ids;
+ i < gids->i_cnt; i++, ids++) {
+ if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
+ member = B_TRUE;
+ break;
+ }
}
- if (smb_lgrp_is_member(&lgrp, user_sid)) {
- ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid);
- if (ids->i_sid == NULL) {
+ if (member && (new_gids.i_cnt < (gids->i_cnt + gcnt))) {
+ new_ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid);
+ if (new_ids->i_sid == NULL) {
smb_lgrp_free(&lgrp);
- return (NT_STATUS_NO_MEMORY);
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
}
- ids->i_attrs = lgrp.sg_attr;
- gids->i_cnt++;
- gcnt--;
- ids++;
+ new_ids->i_attrs = lgrp.sg_attr;
+ new_ids++;
+ new_gids.i_cnt++;
}
smb_lgrp_free(&lgrp);
}
+
+out:
smb_lgrp_iterclose(&gi);
+ if (ret != 0) {
+ if (new_gids.i_ids != NULL) {
+ /*
+ * Free only the new sids we added.
+ * The old ones were copied ptrs.
+ */
+ ids = new_gids.i_ids + gids->i_cnt;
+ for (i = 0; i < gcnt; i++, ids++) {
+ smb_sid_free(ids->i_sid);
+ }
+ free(new_gids.i_ids);
+ }
+ return (ret);
+ }
+
+ /*
+ * Success! Update passed gids and
+ * free the old array.
+ */
+ free(gids->i_ids);
+ *gids = new_gids;
+
return (NT_STATUS_SUCCESS);
}
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_util.c b/usr/src/lib/smbsrv/libsmb/common/smb_util.c
index 27d770912a..3bd80e4425 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_util.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_util.c
@@ -77,11 +77,9 @@ static void smb_log_trace(int, const char *);
static smb_log_t *smb_log_get(smb_log_hdl_t);
static void smb_log_dump(smb_log_t *);
-static uint_t smb_make_mask(char *, uint_t);
-static boolean_t smb_netmatch(struct netbuf *, char *);
static boolean_t smb_netgroup_match(struct nd_hostservlist *, char *, int);
-extern int __multi_innetgr();
+extern int __multi_innetgr();
extern int __netdir_getbyaddr_nosrv(struct netconfig *,
struct nd_hostservlist **, struct netbuf *);
@@ -413,54 +411,80 @@ rand_hash(
int
smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
{
- int nentries;
- char *gr;
- char *lasts;
+ char addr[INET_ADDRSTRLEN];
+ char buff[256];
+ char *cstr = access_list, *gr = access_list;
char *host;
- int off;
+ int clres;
int i;
+ int nentries = 0;
+ int off;
int response;
- int clres;
+ int sbr = 0;
struct nd_hostservlist *clnames;
struct in_addr inaddr;
struct sockaddr_in sa;
+ struct sockaddr_in6 sa6;
struct netbuf buf;
struct netconfig *config;
+ struct netent n, *np;
if (access_list == NULL)
return (0);
- inaddr.s_addr = ipaddr->a_ipv4;
-
- /*
- * If access list is empty or "*" - then it's "all"
- */
+ /* If access list is empty or "*" - then it's "all" */
if (*access_list == '\0' || strcmp(access_list, "*") == 0)
return (-1);
- nentries = 0;
-
- sa.sin_family = AF_INET;
- sa.sin_port = 0;
- sa.sin_addr = inaddr;
-
- buf.len = buf.maxlen = sizeof (sa);
- buf.buf = (char *)&sa;
+ switch (ipaddr->a_family) {
+ case AF_INET:
+ inaddr.s_addr = ipaddr->a_ipv4;
+ sa.sin_family = AF_INET;
+ sa.sin_port = 0;
+ sa.sin_addr = inaddr;
+ buf.len = buf.maxlen = sizeof (sa);
+ buf.buf = (char *)&sa;
+ config = getnetconfigent("tcp");
+ break;
+ case AF_INET6:
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_port = 0;
+ sa6.sin6_addr = ipaddr->a_ipv6;
+ buf.len = buf.maxlen = sizeof (sa6);
+ buf.buf = (char *)&sa6;
+ config = getnetconfigent("tcp6");
+ break;
+ default:
+ return (1);
+ }
- config = getnetconfigent("tcp");
if (config == NULL)
return (1);
+ /* Try to lookup client hostname */
clres = __netdir_getbyaddr_nosrv(config, &clnames, &buf);
freenetconfigent(config);
- for (gr = strtok_r(access_list, ":", &lasts);
- gr != NULL; gr = strtok_r(NULL, ":", &lasts)) {
+ for (;;) {
+ if ((cstr = strpbrk(cstr, "[]:")) != NULL) {
+ switch (*cstr) {
+ case '[':
+ case ']':
+ sbr = !sbr;
+ cstr++;
+ continue;
+ case ':':
+ if (sbr) {
+ cstr++;
+ continue;
+ }
+ *cstr = '\0';
+ }
+ }
/*
- * If the list name has a '-' prepended
- * then a match of the following name
- * implies failure instead of success.
+ * If the list name has a '-' prepended then a match of
+ * the following name implies failure instead of success.
*/
if (*gr == '-') {
response = 0;
@@ -470,21 +494,53 @@ smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
}
/*
- * First check if we have '@' entry, as smb_netmatch doesn't
- * care if client address can be resolved.
+ * First check if we have '@' entry, as it doesn't
+ * require client hostname.
*/
- if (*gr == '@')
- if (smb_netmatch(&buf, gr + 1))
- return (response);
+ if (*gr == '@') {
+ gr++;
+
+ if (!isdigit(*gr) && *gr != '[') {
+ /* Netname support */
+ if ((np = getnetbyname_r(gr, &n, buff,
+ sizeof (buff))) != NULL &&
+ np->n_net != 0) {
+ while ((np->n_net & 0xFF000000u) == 0)
+ np->n_net <<= 8;
+ np->n_net = htonl(np->n_net);
+ if (inet_ntop(AF_INET, &np->n_net, addr,
+ INET_ADDRSTRLEN) == NULL)
+ break;
+ if (inet_matchaddr(buf.buf, addr))
+ return (response);
+ }
+ } else {
+ if (inet_matchaddr(buf.buf, gr))
+ return (response);
+ }
+
+ if (cstr == NULL)
+ break;
+
+ gr = ++cstr;
+
+ continue;
+ }
+
/*
* No other checks can be performed if client address
* can't be resolved.
*/
- if (clres)
+ if (clres) {
+ if (cstr == NULL)
+ break;
+
+ gr = ++cstr;
+
continue;
- /*
- * Otherwise loop through all client hostname aliases.
- */
+ }
+
+ /* Otherwise loop through all client hostname aliases */
for (i = 0; i < clnames->h_cnt; i++) {
host = clnames->h_hostservs[i].h_host;
/*
@@ -494,7 +550,7 @@ smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
* suffix.
*/
if (*gr == '.') {
- if (*(gr + 1) == '\0') { /* single dot */
+ if (*(gr + 1) == '\0') {
if (strchr(host, '.') == NULL)
return (response);
} else {
@@ -505,15 +561,18 @@ smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
}
}
} else {
- /*
- * Just do a hostname match
- */
+ /* Just do a hostname match */
if (strcasecmp(gr, host) == 0)
return (response);
}
}
nentries++;
+
+ if (cstr == NULL)
+ break;
+
+ gr = ++cstr;
}
if (clres)
@@ -523,116 +582,6 @@ smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
}
/*
- * smb_make_mask
- *
- * Construct a mask for an IPv4 address using the @<dotted-ip>/<len>
- * syntax or use the default mask for the IP address.
- */
-static uint_t
-smb_make_mask(char *maskstr, uint_t addr)
-{
- uint_t mask;
- uint_t bits;
-
- /*
- * If the mask is specified explicitly then
- * use that value, e.g.
- *
- * @109.104.56/28
- *
- * otherwise assume a mask from the zero octets
- * in the least significant bits of the address, e.g.
- *
- * @109.104 or @109.104.0.0
- */
- if (maskstr) {
- bits = atoi(maskstr);
- mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits)
- : 0;
- addr &= mask;
- } else {
- if ((addr & IN_CLASSA_HOST) == 0)
- mask = IN_CLASSA_NET;
- else if ((addr & IN_CLASSB_HOST) == 0)
- mask = IN_CLASSB_NET;
- else if ((addr & IN_CLASSC_HOST) == 0)
- mask = IN_CLASSC_NET;
- else
- mask = IN_CLASSE_NET;
- }
-
- return (mask);
-}
-
-/*
- * smb_netmatch
- *
- * Check to see if the address in the netbuf matches the "net"
- * specified by name. The format of "name" can be:
- * fully qualified domain name
- * dotted IP address
- * dotted IP address followed by '/<len>'
- * See sharen_nfs(1M) for details.
- */
-
-static boolean_t
-smb_netmatch(struct netbuf *nb, char *name)
-{
- uint_t claddr;
- struct netent n, *np;
- char *mp, *p;
- uint_t addr, mask;
- int i;
- char buff[256];
-
- /*
- * Check if it's an IPv4 addr
- */
- if (nb->len != sizeof (struct sockaddr_in))
- return (B_FALSE);
-
- (void) memcpy(&claddr,
- /* LINTED pointer alignment */
- &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr,
- sizeof (struct in_addr));
- claddr = ntohl(claddr);
-
- mp = strchr(name, '/');
- if (mp)
- *mp++ = '\0';
-
- if (isdigit(*name)) {
- /*
- * Convert a dotted IP address
- * to an IP address. The conversion
- * is not the same as that in inet_addr().
- */
- p = name;
- addr = 0;
- for (i = 0; i < 4; i++) {
- addr |= atoi(p) << ((3-i) * 8);
- p = strchr(p, '.');
- if (p == NULL)
- break;
- p++;
- }
- } else {
- /*
- * Turn the netname into
- * an IP address.
- */
- np = getnetbyname_r(name, &n, buff, sizeof (buff));
- if (np == NULL) {
- return (B_FALSE);
- }
- addr = np->n_net;
- }
-
- mask = smb_make_mask(mp, addr);
- return ((claddr & mask) == addr);
-}
-
-/*
* smb_netgroup_match
*
* Check whether any of the hostnames in clnames are