diff options
author | Tom Erickson <Tom.Erickson@Sun.COM> | 2009-11-09 20:45:32 -0800 |
---|---|---|
committer | Tom Erickson <Tom.Erickson@Sun.COM> | 2009-11-09 20:45:32 -0800 |
commit | 92241e0b80813d0b83c08e730a29b9d1831794fc (patch) | |
tree | ae911ea5f6928508430420730ad8dba20b5b3aa2 /usr/src/lib/libzfs | |
parent | 8d4cf8d8d2965ea43bccdc838f15c18634fee02d (diff) | |
download | illumos-gate-92241e0b80813d0b83c08e730a29b9d1831794fc.tar.gz |
PSARC 2009/510 ZFS received properties
6850025 want to preserve local properties for zfs_recv
6839260 want zfs send with properties
6855486 zfs_receive should keep trying to set properties even after some fail
6850030 snapshots on read-only dataset shouldn't affect zfs_receive
Diffstat (limited to 'usr/src/lib/libzfs')
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs.h | 38 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_dataset.c | 314 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_impl.h | 1 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_sendrecv.c | 166 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_util.c | 33 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/mapfile-vers | 1 |
6 files changed, 406 insertions, 147 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index 2ea71273f2..d025447ccd 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -382,6 +382,8 @@ extern const char *zfs_prop_to_name(zfs_prop_t); extern int zfs_prop_set(zfs_handle_t *, const char *, const char *); extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, zprop_source_t *, char *, size_t, boolean_t); +extern int zfs_prop_get_recvd(zfs_handle_t *, const char *, char *, size_t, + boolean_t); extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *, zprop_source_t *, char *, size_t); extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, @@ -389,10 +391,11 @@ extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname, extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, char *propbuf, int proplen, boolean_t literal); extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t); -extern int zfs_prop_inherit(zfs_handle_t *, const char *); +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 *); typedef struct zprop_list { int pl_prop; @@ -400,10 +403,11 @@ typedef struct zprop_list { struct zprop_list *pl_next; boolean_t pl_all; size_t pl_width; + size_t pl_recvd_width; boolean_t pl_fixed; } zprop_list_t; -extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **); +extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **, boolean_t); extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *); #define ZFS_MOUNTPOINT_NONE "none" @@ -427,13 +431,24 @@ extern int zprop_get_list(libzfs_handle_t *, char *, zprop_list_t **, zfs_type_t); extern void zprop_free_list(zprop_list_t *); +#define ZFS_GET_NCOLS 5 + +typedef enum { + GET_COL_NONE, + GET_COL_NAME, + GET_COL_PROPERTY, + GET_COL_VALUE, + GET_COL_RECVD, + GET_COL_SOURCE +} zfs_get_column_t; + /* * Functions for printing zfs or zpool properties */ typedef struct zprop_get_cbdata { int cb_sources; - int cb_columns[4]; - int cb_colwidths[5]; + zfs_get_column_t cb_columns[ZFS_GET_NCOLS]; + int cb_colwidths[ZFS_GET_NCOLS + 1]; boolean_t cb_scripted; boolean_t cb_literal; boolean_t cb_first; @@ -442,12 +457,8 @@ typedef struct zprop_get_cbdata { } zprop_get_cbdata_t; void zprop_print_one_property(const char *, zprop_get_cbdata_t *, - const char *, const char *, zprop_source_t, const char *); - -#define GET_COL_NAME 1 -#define GET_COL_PROPERTY 2 -#define GET_COL_VALUE 3 -#define GET_COL_SOURCE 4 + const char *, const char *, zprop_source_t, const char *, + const char *); /* * Iterator functions. @@ -477,17 +488,20 @@ typedef struct sendflags { /* print informational messages (ie, -v was specified) */ int verbose : 1; - /* recursive send (i.e., -R) */ + /* recursive send (ie, -R) */ int replicate : 1; /* for incrementals, do all intermediate snapshots */ - int doall : 1; /* (i.e., -I) */ + int doall : 1; /* (ie, -I) */ /* if dataset is a clone, do incremental from its origin */ int fromorigin : 1; /* do deduplication */ int dedup : 1; + + /* send properties (ie, -p) */ + int props : 1; } sendflags_t; typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *); diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index 9cf2a330cb..9fd78dad82 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -336,6 +336,44 @@ get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) return (0); } +/* + * Utility function to get the received properties of the given object. + */ +static int +get_recvd_props_ioctl(zfs_handle_t *zhp) +{ + libzfs_handle_t *hdl = zhp->zfs_hdl; + nvlist_t *recvdprops; + zfs_cmd_t zc = { 0 }; + int err; + + if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) + return (-1); + + (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + + while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) { + if (errno == ENOMEM) { + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + return (-1); + } + } else { + zcmd_free_nvlists(&zc); + return (-1); + } + } + + err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops); + zcmd_free_nvlists(&zc); + if (err != 0) + return (-1); + + nvlist_free(zhp->zfs_recvd_props); + zhp->zfs_recvd_props = recvdprops; + + return (0); +} + static int put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc) { @@ -522,6 +560,7 @@ zfs_close(zfs_handle_t *zhp) free(zhp->zfs_mntopts); nvlist_free(zhp->zfs_props); nvlist_free(zhp->zfs_user_props); + nvlist_free(zhp->zfs_recvd_props); free(zhp); } @@ -1218,6 +1257,82 @@ error: return (NULL); } +void +zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, + char *errbuf) +{ + switch (err) { + + case ENOSPC: + /* + * For quotas and reservations, ENOSPC indicates + * something different; setting a quota or reservation + * doesn't use any disk space. + */ + switch (prop) { + case ZFS_PROP_QUOTA: + case ZFS_PROP_REFQUOTA: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "size is less than current used or " + "reserved space")); + (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); + break; + + case ZFS_PROP_RESERVATION: + case ZFS_PROP_REFRESERVATION: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "size is greater than available space")); + (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); + break; + + default: + (void) zfs_standard_error(hdl, err, errbuf); + break; + } + break; + + case EBUSY: + (void) zfs_standard_error(hdl, EBUSY, errbuf); + break; + + case EROFS: + (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); + break; + + case ENOTSUP: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "pool and or dataset must be upgraded to set this " + "property or value")); + (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); + break; + + case ERANGE: + if (prop == ZFS_PROP_COMPRESSION) { + (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "property setting is not allowed on " + "bootable datasets")); + (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); + } else { + (void) zfs_standard_error(hdl, err, errbuf); + } + break; + + case EOVERFLOW: + /* + * This platform can't address a volume this big. + */ +#ifdef _ILP32 + if (prop == ZFS_PROP_VOLSIZE) { + (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); + break; + } +#endif + /* FALLTHROUGH */ + default: + (void) zfs_standard_error(hdl, err, errbuf); + } +} + /* * Given a property name and value, set the property for the given dataset. */ @@ -1286,76 +1401,7 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); if (ret != 0) { - switch (errno) { - - case ENOSPC: - /* - * For quotas and reservations, ENOSPC indicates - * something different; setting a quota or reservation - * doesn't use any disk space. - */ - switch (prop) { - case ZFS_PROP_QUOTA: - case ZFS_PROP_REFQUOTA: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "size is less than current used or " - "reserved space")); - (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); - break; - - case ZFS_PROP_RESERVATION: - case ZFS_PROP_REFRESERVATION: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "size is greater than available space")); - (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf); - break; - - default: - (void) zfs_standard_error(hdl, errno, errbuf); - break; - } - break; - - case EBUSY: - (void) zfs_standard_error(hdl, EBUSY, errbuf); - break; - - case EROFS: - (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf); - break; - - case ENOTSUP: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "pool and or dataset must be upgraded to set this " - "property or value")); - (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); - break; - - case ERANGE: - if (prop == ZFS_PROP_COMPRESSION) { - (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property setting is not allowed on " - "bootable datasets")); - (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); - } else { - (void) zfs_standard_error(hdl, errno, errbuf); - } - break; - - case EOVERFLOW: - /* - * This platform can't address a volume this big. - */ -#ifdef _ILP32 - if (prop == ZFS_PROP_VOLSIZE) { - (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf); - break; - } -#endif - /* FALLTHROUGH */ - default: - (void) zfs_standard_error(hdl, errno, errbuf); - } + zfs_setprop_error(hdl, prop, errno, errbuf); } else { if (do_prefix) ret = changelist_postfix(cl); @@ -1377,10 +1423,11 @@ error: } /* - * Given a property, inherit the value from the parent dataset. + * Given a property, inherit the value from the parent dataset, or if received + * is TRUE, revert to the received value, if any. */ int -zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) +zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received) { zfs_cmd_t zc = { 0 }; int ret; @@ -1392,6 +1439,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot inherit %s for '%s'"), propname, zhp->zfs_name); + zc.zc_cookie = received; if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) { /* * For user properties, the amount of work we have to do is very @@ -1418,7 +1466,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname) if (zfs_prop_readonly(prop)) return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf)); - if (!zfs_prop_inheritable(prop)) + if (!zfs_prop_inheritable(prop) && !received) return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf)); /* @@ -1523,6 +1571,26 @@ getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) return (value); } +static boolean_t +zfs_is_recvd_props_mode(zfs_handle_t *zhp) +{ + return (zhp->zfs_props == zhp->zfs_recvd_props); +} + +static void +zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) +{ + *cookie = (uint64_t)(uintptr_t)zhp->zfs_props; + zhp->zfs_props = zhp->zfs_recvd_props; +} + +static void +zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie) +{ + zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie; + *cookie = 0; +} + /* * Internal function for getting a numeric property. Both zfs_prop_get() and * zfs_prop_get_int() are built using this interface. @@ -1541,6 +1609,7 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, struct mnttab mnt; char *mntopt_on = NULL; char *mntopt_off = NULL; + boolean_t received = zfs_is_recvd_props_mode(zhp); *source = NULL; @@ -1616,6 +1685,9 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, case ZFS_PROP_NBMAND: *val = getprop_uint64(zhp, prop, source); + if (received) + break; + if (hasmntopt(&mnt, mntopt_on) && !*val) { *val = B_TRUE; if (src) @@ -1628,22 +1700,16 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, break; case ZFS_PROP_CANMOUNT: - *val = getprop_uint64(zhp, prop, source); - if (*val != ZFS_CANMOUNT_ON) - *source = zhp->zfs_name; - else - *source = ""; /* default */ - break; - case ZFS_PROP_QUOTA: case ZFS_PROP_REFQUOTA: case ZFS_PROP_RESERVATION: case ZFS_PROP_REFRESERVATION: *val = getprop_uint64(zhp, prop, source); - if (*val == 0) - *source = ""; /* default */ - else + + if (*source == NULL) { + /* not default, must be local */ *source = zhp->zfs_name; + } break; case ZFS_PROP_MOUNTED: @@ -1719,6 +1785,8 @@ get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, *srctype = ZPROP_SRC_NONE; } else if (source[0] == '\0') { *srctype = ZPROP_SRC_DEFAULT; + } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) { + *srctype = ZPROP_SRC_RECEIVED; } else { if (strcmp(source, zhp->zfs_name) == 0) { *srctype = ZPROP_SRC_LOCAL; @@ -1730,6 +1798,43 @@ get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source, } +int +zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf, + size_t proplen, boolean_t literal) +{ + zfs_prop_t prop; + int err = 0; + + if (zhp->zfs_recvd_props == NULL) + if (get_recvd_props_ioctl(zhp) != 0) + return (-1); + + prop = zfs_name_to_prop(propname); + + if (prop != ZPROP_INVAL) { + uint64_t cookie; + if (!nvlist_exists(zhp->zfs_recvd_props, propname)) + return (-1); + zfs_set_recvd_props_mode(zhp, &cookie); + 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; + if (nvlist_lookup_nvlist(zhp->zfs_recvd_props, + propname, &propval) != 0) + return (-1); + verify(nvlist_lookup_string(propval, ZPROP_VALUE, + &recvdval) == 0); + (void) strlcpy(propbuf, recvdval, proplen); + } + + return (err == 0 ? 0 : -1); +} + /* * Retrieve a property from the given object. If 'literal' is specified, then * numbers are left as exact values. Otherwise, numbers are converted to a @@ -1745,6 +1850,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, uint64_t val; char *str; const char *strval; + boolean_t received = zfs_is_recvd_props_mode(zhp); /* * Check to see if this property applies to our object @@ -1752,6 +1858,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) return (-1); + if (received && zfs_prop_readonly(prop)) + return (-1); + if (src) *src = ZPROP_SRC_NONE; @@ -3478,6 +3587,15 @@ zfs_get_user_props(zfs_handle_t *zhp) return (zhp->zfs_user_props); } +nvlist_t * +zfs_get_recvd_props(zfs_handle_t *zhp) +{ + if (zhp->zfs_recvd_props == NULL) + if (get_recvd_props_ioctl(zhp) != 0) + return (NULL); + return (zhp->zfs_recvd_props); +} + /* * This function is used by 'zfs list' to determine the exact set of columns to * display, and their maximum widths. This does two main things: @@ -3487,10 +3605,12 @@ zfs_get_user_props(zfs_handle_t *zhp) * for new unique user properties and add them to the list. * * - For non fixed-width properties, keep track of the maximum width seen - * so that we can size the column appropriately. + * so that we can size the column appropriately. If the user has + * requested received property values, we also need to compute the width + * of the RECEIVED column. */ int -zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp) +zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received) { libzfs_handle_t *hdl = zhp->zfs_hdl; zprop_list_t *entry; @@ -3561,12 +3681,24 @@ zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp) if (strlen(buf) > entry->pl_width) entry->pl_width = strlen(buf); } - } else if (nvlist_lookup_nvlist(userprops, - entry->pl_user_prop, &propval) == 0) { - verify(nvlist_lookup_string(propval, - ZPROP_VALUE, &strval) == 0); - if (strlen(strval) > entry->pl_width) - entry->pl_width = strlen(strval); + if (received && zfs_prop_get_recvd(zhp, + zfs_prop_to_name(entry->pl_prop), + buf, sizeof (buf), B_FALSE) == 0) + if (strlen(buf) > entry->pl_recvd_width) + entry->pl_recvd_width = strlen(buf); + } else { + if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop, + &propval) == 0) { + verify(nvlist_lookup_string(propval, + ZPROP_VALUE, &strval) == 0); + if (strlen(strval) > entry->pl_width) + entry->pl_width = strlen(strval); + } + if (received && zfs_prop_get_recvd(zhp, + entry->pl_user_prop, + buf, sizeof (buf), B_FALSE) == 0) + if (strlen(buf) > entry->pl_recvd_width) + entry->pl_recvd_width = strlen(buf); } } diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h index 3644677d60..ef34591fe3 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -92,6 +92,7 @@ struct zfs_handle { dmu_objset_stats_t zfs_dmustats; nvlist_t *zfs_props; nvlist_t *zfs_user_props; + nvlist_t *zfs_recvd_props; boolean_t zfs_mntcheck; char *zfs_mntopts; uint8_t *zfs_props_table; diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c index 4aa727efe0..79263bae6c 100644 --- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c +++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c @@ -46,6 +46,9 @@ #include "libzfs_impl.h" #include <sha2.h> +/* 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, int, avl_tree_t *, char **); @@ -510,6 +513,7 @@ typedef struct send_data { nvlist_t *snapprops; const char *fromsnap; const char *tosnap; + boolean_t recursive; /* * The header nvlist is of the following format: @@ -597,18 +601,30 @@ send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv) if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION || prop == ZFS_PROP_REFQUOTA || prop == ZFS_PROP_REFRESERVATION) { - /* these guys are modifyable, but have no source */ + char *source; uint64_t value; verify(nvlist_lookup_uint64(propnv, ZPROP_VALUE, &value) == 0); if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) continue; + /* + * May have no source before SPA_VERSION_RECVD_PROPS, + * but is still modifiable. + */ + if (nvlist_lookup_string(propnv, + ZPROP_SOURCE, &source) == 0) { + if ((strcmp(source, zhp->zfs_name) != 0) && + (strcmp(source, + ZPROP_SOURCE_VAL_RECVD) != 0)) + continue; + } } else { char *source; if (nvlist_lookup_string(propnv, ZPROP_SOURCE, &source) != 0) continue; - if (strcmp(source, zhp->zfs_name) != 0) + if ((strcmp(source, zhp->zfs_name) != 0) && + (strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0)) continue; } @@ -637,7 +653,7 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg) { send_data_t *sd = arg; nvlist_t *nvfs, *nv; - int rv; + int rv = 0; uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid; uint64_t guid = zhp->zfs_dmustats.dds_guid; char guidstring[64]; @@ -679,7 +695,8 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg) nvlist_free(nvfs); /* iterate over children */ - rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd); + if (sd->recursive) + rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd); sd->parent_fromsnap_guid = parent_fromsnap_guid_save; @@ -689,7 +706,7 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg) static int gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap, - const char *tosnap, nvlist_t **nvlp, avl_tree_t **avlp) + const char *tosnap, boolean_t recursive, nvlist_t **nvlp, avl_tree_t **avlp) { zfs_handle_t *zhp; send_data_t sd = { 0 }; @@ -702,6 +719,7 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap, VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0)); sd.fromsnap = fromsnap; sd.tosnap = tosnap; + sd.recursive = recursive; if ((error = send_iterate_fs(zhp, &sd)) != 0) { nvlist_free(sd.fss); @@ -1045,12 +1063,12 @@ again: origin_nv = fsavl_find(sdd->fsavl, origin_guid, NULL); if (origin_nv && nvlist_lookup_boolean(origin_nv, "sent") == ENOENT) { - /* - * origin has not been sent yet; - * skip this clone. - */ - needagain = B_TRUE; - continue; + /* + * origin has not been sent yet; + * skip this clone. + */ + needagain = B_TRUE; + continue; } zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET); @@ -1084,7 +1102,7 @@ again: * uses a special header (with a hdrtype field of DMU_COMPOUNDSTREAM) * if "replicate" is set. If "doall" is set, dump all the intermediate * snapshots. The DMU_COMPOUNDSTREAM header is used in the "doall" - * case too. + * case too. If "props" is set, send properties. */ int zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, @@ -1137,14 +1155,12 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, } } - if (flags.replicate || flags.doall) { + 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 }; - assert(fromsnap || flags.doall); - if (holdsnaps) { (void) snprintf(holdtag, sizeof (holdtag), ".send-%d-%llu", getpid(), (u_longlong_t)holdseq); @@ -1155,8 +1171,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, goto err_out; } - - if (flags.replicate) { + if (flags.replicate || flags.props) { nvlist_t *hdrnv; VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0)); @@ -1165,9 +1180,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) { + VERIFY(0 == nvlist_add_boolean(hdrnv, + "not_recursive")); + } err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name, - fromsnap, tosnap, &fss, &fsavl); + fromsnap, tosnap, flags.replicate, &fss, &fsavl); if (err) { if (holdsnaps) { (void) zfs_release_range(zhp, fromsnap, @@ -1204,7 +1223,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, err = cksum_and_write(&drr, sizeof (drr), &zc, outfd); /* write header nvlist */ - if (err != -1 && flags.replicate) { + if (err != -1 && packbuf != NULL) { err = cksum_and_write(packbuf, buflen, &zc, outfd); } free(packbuf); @@ -1261,7 +1280,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, (void) close(pipefd[0]); (void) pthread_join(tid, NULL); } - if (flags.replicate || flags.doall) { + + if (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 @@ -1471,12 +1491,13 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, changelist_free(clp); /* - * Deferred destroy should always succeed. Since we can't tell - * if it destroyed the dataset or just marked it for deferred - * destroy, always do the rename just in case. + * Deferred destroy might destroy the snapshot or only mark it to be + * destroyed later, and it returns success in either case. */ - if (err != 0 || defer) + if (err != 0 || (defer && zfs_dataset_exists(hdl, name, + ZFS_TYPE_SNAPSHOT))) { err = recv_rename(hdl, name, NULL, baselen, newname, flags); + } return (err); } @@ -1592,12 +1613,15 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs, char *tosnap, *fromsnap; char newname[ZFS_MAXNAMELEN]; int error; - boolean_t needagain, progress; + boolean_t needagain, progress, recursive; char *s1, *s2; VERIFY(0 == nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap)); VERIFY(0 == nvlist_lookup_string(stream_nv, "tosnap", &tosnap)); + recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") == + ENOENT); + if (flags.dryrun) return (0); @@ -1605,7 +1629,7 @@ again: needagain = progress = B_FALSE; if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL, - &local_nv, &local_avl)) != 0) + recursive, &local_nv, &local_avl)) != 0) return (error); /* @@ -1728,7 +1752,7 @@ again: stream_snapname, &props)) { zfs_cmd_t zc = { 0 }; - zc.zc_cookie = B_TRUE; /* clear current props */ + zc.zc_cookie = B_TRUE; /* received */ (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s", fsname, nvpair_name(snapelem)); if (zcmd_write_src_nvlist(hdl, &zc, @@ -1877,12 +1901,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, * Read in the nvlist from the stream. */ if (drr->drr_payloadlen != 0) { - if (!flags.isprefix) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "must use -d to receive replication " - "(send -R) stream")); - return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); - } + boolean_t recursive; error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen, &stream_nv, flags.byteswap, zc); @@ -1890,6 +1909,16 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, error = zfs_error(hdl, EZFS_BADSTREAM, errbuf); goto out; } + + recursive = (nvlist_lookup_boolean(stream_nv, + "not_recursive") == ENOENT); + + if (recursive && !flags.isprefix) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "must use -d to receive replication " + "(send -R) stream")); + return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); + } } /* @@ -1989,6 +2018,19 @@ out: return (error); } +static void +trunc_prop_errs(int truncated) +{ + ASSERT(truncated != 0); + + if (truncated == 1) + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "1 more property could not be set\n")); + else + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "%d more properties could not be set\n"), truncated); +} + static int recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap) { @@ -2072,12 +2114,14 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, char *cp; struct drr_begin *drrb = &drr->drr_u.drr_begin; char errbuf[1024]; + char prop_errbuf[1024]; char chopprefix[ZFS_MAXNAMELEN]; boolean_t newfs = B_FALSE; boolean_t stream_wantsnewfs; uint64_t parent_snapguid = 0; prop_changelist_t *clp = NULL; nvlist_t *snapprops_nvlist = NULL; + zprop_errflags_t prop_errflags; begin_time = time(NULL); @@ -2337,14 +2381,52 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, return (recv_skip(hdl, infd, flags.byteswap)); } + zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf; + zc.zc_nvlist_dst_size = sizeof (prop_errbuf); + err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc); ioctl_errno = errno; + prop_errflags = (zprop_errflags_t)zc.zc_obj; + + if (err == 0) { + nvlist_t *prop_errors; + VERIFY(0 == nvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst, + zc.zc_nvlist_dst_size, &prop_errors, 0)); + + nvpair_t *prop_err = NULL; + + while ((prop_err = nvlist_next_nvpair(prop_errors, + prop_err)) != NULL) { + char tbuf[1024]; + zfs_prop_t prop; + int intval; + + prop = zfs_name_to_prop(nvpair_name(prop_err)); + (void) nvpair_value_int32(prop_err, &intval); + if (strcmp(nvpair_name(prop_err), + ZPROP_N_MORE_ERRORS) == 0) { + trunc_prop_errs(intval); + break; + } else { + (void) snprintf(tbuf, sizeof (tbuf), + dgettext(TEXT_DOMAIN, + "cannot receive %s property on %s"), + nvpair_name(prop_err), zc.zc_name); + zfs_setprop_error(hdl, prop, intval, tbuf); + } + } + nvlist_free(prop_errors); + } + + zc.zc_nvlist_dst = 0; + zc.zc_nvlist_dst_size = 0; zcmd_free_nvlists(&zc); if (err == 0 && snapprops_nvlist) { zfs_cmd_t zc2 = { 0 }; (void) strcpy(zc2.zc_name, zc.zc_value); + zc2.zc_cookie = B_TRUE; /* received */ if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) { (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2); zcmd_free_nvlists(&zc2); @@ -2367,7 +2449,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, * get a strange "does not exist" error message. */ *cp = '\0'; - if (gather_nvlist(hdl, zc.zc_value, NULL, NULL, + if (gather_nvlist(hdl, zc.zc_value, NULL, NULL, B_FALSE, &local_nv, &local_avl) == 0) { *cp = '@'; fs = fsavl_find(local_avl, drrb->drr_toguid, NULL); @@ -2379,14 +2461,13 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, (void) printf("snap %s already exists; " "ignoring\n", zc.zc_value); } - ioctl_err = recv_skip(hdl, infd, + err = ioctl_err = recv_skip(hdl, infd, flags.byteswap); } } *cp = '@'; } - if (ioctl_err != 0) { switch (ioctl_errno) { case ENODEV: @@ -2463,6 +2544,19 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, changelist_free(clp); } + if (prop_errflags & ZPROP_ERR_NOCLEAR) { + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: " + "failed to clear unreceived properties on %s"), + zc.zc_name); + (void) fprintf(stderr, "\n"); + } + if (prop_errflags & ZPROP_ERR_NORESTORE) { + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: " + "failed to restore original properties on %s"), + zc.zc_name); + (void) fprintf(stderr, "\n"); + } + if (err || ioctl_err) return (-1); diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index b21e7236dd..f7beab37dd 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -380,7 +380,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...) zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap); break; default: - zfs_error_aux(hdl, strerror(errno)); + zfs_error_aux(hdl, strerror(error)); zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap); break; } @@ -815,6 +815,8 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) "PROPERTY")); cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN, "VALUE")); + cbp->cb_colwidths[GET_COL_RECVD] = strlen(dgettext(TEXT_DOMAIN, + "RECEIVED")); cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN, "SOURCE")); @@ -828,7 +830,7 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) * inheriting from the longest name. This is acceptable because in the * majority of cases 'SOURCE' is the last column displayed, and we don't * use the width anyway. Note that the 'VALUE' column can be oversized, - * if the name of the property is much longer the any values we find. + * if the name of the property is much longer than any values we find. */ for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { /* @@ -859,6 +861,11 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE]) cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width; + /* 'RECEIVED' column. */ + if (pl != cbp->cb_proplist && + pl->pl_recvd_width > cbp->cb_colwidths[GET_COL_RECVD]) + cbp->cb_colwidths[GET_COL_RECVD] = pl->pl_recvd_width; + /* * 'NAME' and 'SOURCE' columns */ @@ -874,7 +881,7 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) /* * Now go through and print the headers. */ - for (i = 0; i < 4; i++) { + for (i = 0; i < ZFS_GET_NCOLS; i++) { switch (cbp->cb_columns[i]) { case GET_COL_NAME: title = dgettext(TEXT_DOMAIN, "NAME"); @@ -885,6 +892,9 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) case GET_COL_VALUE: title = dgettext(TEXT_DOMAIN, "VALUE"); break; + case GET_COL_RECVD: + title = dgettext(TEXT_DOMAIN, "RECEIVED"); + break; case GET_COL_SOURCE: title = dgettext(TEXT_DOMAIN, "SOURCE"); break; @@ -893,7 +903,8 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) } if (title != NULL) { - if (i == 3 || cbp->cb_columns[i + 1] == 0) + if (i == (ZFS_GET_NCOLS - 1) || + cbp->cb_columns[i + 1] == GET_COL_NONE) (void) printf("%s", title); else (void) printf("%-*s ", @@ -911,7 +922,7 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) void zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp, const char *propname, const char *value, zprop_source_t sourcetype, - const char *source) + const char *source, const char *recvd_value) { int i; const char *str; @@ -926,7 +937,7 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp, if (cbp->cb_first) zprop_print_headers(cbp, cbp->cb_type); - for (i = 0; i < 4; i++) { + for (i = 0; i < ZFS_GET_NCOLS; i++) { switch (cbp->cb_columns[i]) { case GET_COL_NAME: str = name; @@ -963,14 +974,21 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp, "inherited from %s", source); str = buf; break; + case ZPROP_SRC_RECEIVED: + str = "received"; + break; } break; + case GET_COL_RECVD: + str = (recvd_value == NULL ? "-" : recvd_value); + break; + default: continue; } - if (cbp->cb_columns[i + 1] == 0) + if (cbp->cb_columns[i + 1] == GET_COL_NONE) (void) printf("%s", str); else if (cbp->cb_scripted) (void) printf("%s\t", str); @@ -978,7 +996,6 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp, (void) printf("%-*s ", cbp->cb_colwidths[cbp->cb_columns[i]], str); - } (void) printf("\n"); diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index 320e99bbd9..d6ff0b4c1f 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -102,6 +102,7 @@ SUNWprivate_1.1 { zfs_prop_get; zfs_prop_get_int; zfs_prop_get_numeric; + zfs_prop_get_recvd; zfs_prop_get_table; zfs_prop_get_userquota_int; zfs_prop_get_userquota; |