diff options
author | John Sonnenschein <johns@joyent.com> | 2011-12-13 06:55:43 +0000 |
---|---|---|
committer | John Sonnenschein <johns@joyent.com> | 2011-12-13 06:55:43 +0000 |
commit | 73c531dd5303a633bcaaf609c6eeb2554096b478 (patch) | |
tree | 41cd342a9302e3f92a961716a5b27ba78912e937 /usr/src/lib | |
parent | cf1f80d4478d278b94ad9c99026c1b589864f7da (diff) | |
parent | f113db6b62228353583f701aefa9a8f2acbf64b8 (diff) | |
download | illumos-joyent-73c531dd5303a633bcaaf609c6eeb2554096b478.tar.gz |
Merge branch 'master' into gcc4
Diffstat (limited to 'usr/src/lib')
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, >nd); - 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, >nd); 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 |