PSARC/2008/483 ZFS clone -o
PSARC/2008/484 ZFS snapshot properties
6613766 create-time properties for clones
6701797 want user properties on snapshots
--- a/usr/src/cmd/zdb/zdb.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zdb/zdb.c Fri Aug 01 16:32:18 2008 -0700
@@ -802,6 +802,8 @@
(u_longlong_t)ds->ds_flags);
(void) printf("\t\tnext_clones_obj = %llu\n",
(u_longlong_t)ds->ds_next_clones_obj);
+ (void) printf("\t\tprops_obj = %llu\n",
+ (u_longlong_t)ds->ds_props_obj);
(void) printf("\t\tbp = %s\n", blkbuf);
}
--- a/usr/src/cmd/zfs/zfs_main.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zfs/zfs_main.c Fri Aug 01 16:32:18 2008 -0700
@@ -175,8 +175,8 @@
{
switch (idx) {
case HELP_CLONE:
- return (gettext("\tclone [-p] <snapshot> "
- "<filesystem|volume>\n"));
+ return (gettext("\tclone [-p] [-o property=value] ... "
+ "<snapshot> <filesystem|volume>\n"));
case HELP_CREATE:
return (gettext("\tcreate [-p] [-o property=value] ... "
"<filesystem>\n"
@@ -192,7 +192,7 @@
"[filesystem|volume|snapshot] ...\n"));
case HELP_INHERIT:
return (gettext("\tinherit [-r] <property> "
- "<filesystem|volume> ...\n"));
+ "<filesystem|volume|snapshot> ...\n"));
case HELP_UPGRADE:
return (gettext("\tupgrade [-v]\n"
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
@@ -221,11 +221,11 @@
return (gettext("\tsend [-R] [-[iI] snapshot] <snapshot>\n"));
case HELP_SET:
return (gettext("\tset <property=value> "
- "<filesystem|volume> ...\n"));
+ "<filesystem|volume|snapshot> ...\n"));
case HELP_SHARE:
return (gettext("\tshare <-a | filesystem>\n"));
case HELP_SNAPSHOT:
- return (gettext("\tsnapshot [-r] "
+ return (gettext("\tsnapshot [-r] [-o property=value] ... "
"<filesystem@snapname|volume@snapname>\n"));
case HELP_UNMOUNT:
return (gettext("\tunmount [-f] "
@@ -397,8 +397,35 @@
exit(requested ? 0 : 2);
}
+static int
+parseprop(nvlist_t *props)
+{
+ char *propname = optarg;
+ char *propval, *strval;
+
+ if ((propval = strchr(propname, '=')) == NULL) {
+ (void) fprintf(stderr, gettext("missing "
+ "'=' for -o option\n"));
+ return (-1);
+ }
+ *propval = '\0';
+ propval++;
+ if (nvlist_lookup_string(props, propname, &strval) == 0) {
+ (void) fprintf(stderr, gettext("property '%s' "
+ "specified multiple times\n"), propname);
+ return (-1);
+ }
+ if (nvlist_add_string(props, propname, propval) != 0) {
+ (void) fprintf(stderr, gettext("internal "
+ "error: out of memory\n"));
+ return (-1);
+ }
+ return (0);
+
+}
+
/*
- * zfs clone [-p] <snap> <fs | vol>
+ * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
*
* Given an existing dataset, create a writable copy whose initial contents
* are the same as the source. The newly created dataset maintains a
@@ -410,21 +437,32 @@
static int
zfs_do_clone(int argc, char **argv)
{
- zfs_handle_t *zhp;
+ zfs_handle_t *zhp = NULL;
boolean_t parents = B_FALSE;
+ nvlist_t *props;
int ret;
int c;
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr, gettext("internal error: "
+ "out of memory\n"));
+ return (1);
+ }
+
/* check options */
- while ((c = getopt(argc, argv, "p")) != -1) {
+ while ((c = getopt(argc, argv, "o:p")) != -1) {
switch (c) {
+ case 'o':
+ if (parseprop(props))
+ return (1);
+ break;
case 'p':
parents = B_TRUE;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
- usage(B_FALSE);
+ goto usage;
}
}
@@ -435,16 +473,16 @@
if (argc < 1) {
(void) fprintf(stderr, gettext("missing source dataset "
"argument\n"));
- usage(B_FALSE);
+ goto usage;
}
if (argc < 2) {
(void) fprintf(stderr, gettext("missing target dataset "
"argument\n"));
- usage(B_FALSE);
+ goto usage;
}
if (argc > 2) {
(void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
+ goto usage;
}
/* open the source dataset */
@@ -466,7 +504,7 @@
}
/* pass to libzfs */
- ret = zfs_clone(zhp, argv[1], NULL);
+ ret = zfs_clone(zhp, argv[1], props);
/* create the mountpoint if necessary */
if (ret == 0) {
@@ -481,8 +519,16 @@
}
zfs_close(zhp);
-
- return (ret == 0 ? 0 : 1);
+ nvlist_free(props);
+
+ return (!!ret);
+
+usage:
+ if (zhp)
+ zfs_close(zhp);
+ nvlist_free(props);
+ usage(B_FALSE);
+ return (-1);
}
/*
@@ -511,11 +557,8 @@
boolean_t bflag = B_FALSE;
boolean_t parents = B_FALSE;
int ret = 1;
- nvlist_t *props = NULL;
+ nvlist_t *props;
uint64_t intval;
- char *propname;
- char *propval = NULL;
- char *strval;
int canmount;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
@@ -566,25 +609,8 @@
}
break;
case 'o':
- propname = optarg;
- if ((propval = strchr(propname, '=')) == NULL) {
- (void) fprintf(stderr, gettext("missing "
- "'=' for -o option\n"));
+ if (parseprop(props))
goto error;
- }
- *propval = '\0';
- propval++;
- if (nvlist_lookup_string(props, propname,
- &strval) == 0) {
- (void) fprintf(stderr, gettext("property '%s' "
- "specified multiple times\n"), propname);
- goto error;
- }
- if (nvlist_add_string(props, propname, propval) != 0) {
- (void) fprintf(stderr, gettext("internal "
- "error: out of memory\n"));
- goto error;
- }
break;
case 's':
noreserve = B_TRUE;
@@ -626,6 +652,7 @@
uint64_t spa_version;
char *p;
zfs_prop_t resv_prop;
+ char *strval;
if (p = strchr(argv[0], '/'))
*p = '\0';
@@ -1256,13 +1283,29 @@
*/
static int
-inherit_callback(zfs_handle_t *zhp, void *data)
+inherit_recurse_cb(zfs_handle_t *zhp, void *data)
{
char *propname = data;
int ret;
-
- ret = zfs_prop_inherit(zhp, propname);
- return (ret != 0);
+ zfs_prop_t prop = zfs_name_to_prop(propname);
+
+ /*
+ * If we're doing it recursively, then ignore properties that
+ * are not valid for this type of dataset.
+ */
+ if (prop != ZPROP_INVAL &&
+ !zfs_prop_valid_for_type(prop, zfs_get_type(zhp)))
+ return (0);
+
+ return (zfs_prop_inherit(zhp, propname) != 0);
+}
+
+static int
+inherit_cb(zfs_handle_t *zhp, void *data)
+{
+ char *propname = data;
+
+ return (zfs_prop_inherit(zhp, propname) != 0);
}
static int
@@ -1329,9 +1372,13 @@
usage(B_FALSE);
}
- ret = zfs_for_each(argc, argv, recurse,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL,
- inherit_callback, propname, B_FALSE);
+ if (recurse) {
+ ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_DATASET,
+ NULL, NULL, inherit_recurse_cb, propname, B_FALSE);
+ } else {
+ ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_DATASET,
+ NULL, NULL, inherit_cb, propname, B_FALSE);
+ }
return (ret);
}
@@ -2189,7 +2236,7 @@
}
/*
- * zfs snapshot [-r] <fs@snap>
+ * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
*
* Creates a snapshot with the given name. While functionally equivalent to
* 'zfs create', it is a separate command to differentiate intent.
@@ -2200,17 +2247,28 @@
boolean_t recursive = B_FALSE;
int ret;
char c;
+ nvlist_t *props;
+
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr, gettext("internal error: "
+ "out of memory\n"));
+ return (1);
+ }
/* check options */
- while ((c = getopt(argc, argv, ":r")) != -1) {
+ while ((c = getopt(argc, argv, "ro:")) != -1) {
switch (c) {
+ case 'o':
+ if (parseprop(props))
+ return (1);
+ break;
case 'r':
recursive = B_TRUE;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
- usage(B_FALSE);
+ goto usage;
}
}
@@ -2220,17 +2278,23 @@
/* check number of arguments */
if (argc < 1) {
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
- usage(B_FALSE);
+ goto usage;
}
if (argc > 1) {
(void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
+ goto usage;
}
- ret = zfs_snapshot(g_zfs, argv[0], recursive);
+ ret = zfs_snapshot(g_zfs, argv[0], recursive, props);
+ nvlist_free(props);
if (ret && recursive)
(void) fprintf(stderr, gettext("no snapshots were created\n"));
return (ret != 0);
+
+usage:
+ nvlist_free(props);
+ usage(B_FALSE);
+ return (-1);
}
/*
--- a/usr/src/cmd/zoneadm/zfs.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zoneadm/zfs.c Fri Aug 01 16:32:18 2008 -0700
@@ -304,7 +304,7 @@
if (pre_snapshot(presnapbuf) != Z_OK)
return (Z_ERR);
- res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE);
+ res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL);
if (post_snapshot(postsnapbuf) != Z_OK)
return (Z_ERR);
--- a/usr/src/cmd/zpool/zpool_main.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/cmd/zpool/zpool_main.c Fri Aug 01 16:32:18 2008 -0700
@@ -3462,6 +3462,7 @@
"properties\n"));
(void) printf(gettext(" 10 Cache devices\n"));
(void) printf(gettext(" 11 Improved scrub performance\n"));
+ (void) printf(gettext(" 12 Snapshot properties\n"));
(void) printf(gettext("For more information on a particular "
"version, including supported releases, see:\n\n"));
(void) printf("http://www.opensolaris.org/os/community/zfs/"
--- a/usr/src/grub/grub-0.95/stage2/fsys_zfs.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/grub/grub-0.95/stage2/fsys_zfs.c Fri Aug 01 16:32:18 2008 -0700
@@ -269,8 +269,7 @@
return (-1);
if (uber->ub_magic == UBERBLOCK_MAGIC &&
- uber->ub_version >= SPA_VERSION_1 &&
- uber->ub_version <= SPA_VERSION)
+ uber->ub_version > 0 && uber->ub_version <= SPA_VERSION)
return (0);
return (-1);
--- a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h Fri Aug 01 16:32:18 2008 -0700
@@ -29,18 +29,7 @@
/*
* On-disk version number.
*/
-#define SPA_VERSION_1 1ULL
-#define SPA_VERSION_2 2ULL
-#define SPA_VERSION_3 3ULL
-#define SPA_VERSION_4 4ULL
-#define SPA_VERSION_5 5ULL
-#define SPA_VERSION_6 6ULL
-#define SPA_VERSION_7 7ULL
-#define SPA_VERSION_8 8ULL
-#define SPA_VERSION_9 9ULL
-#define SPA_VERSION_10 10ULL
-#define SPA_VERSION_11 11ULL
-#define SPA_VERSION SPA_VERSION_11
+#define SPA_VERSION 12ULL
/*
* The following are configuration names used in the nvlist describing a pool's
--- a/usr/src/lib/libzfs/common/libzfs.h Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h Fri Aug 01 16:32:18 2008 -0700
@@ -431,7 +431,7 @@
extern int zfs_destroy(zfs_handle_t *);
extern int zfs_destroy_snaps(zfs_handle_t *, char *);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
-extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_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);
extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
extern int zfs_send(zfs_handle_t *, const char *, const char *,
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c Fri Aug 01 16:32:18 2008 -0700
@@ -578,13 +578,6 @@
int chosen_normal = -1;
int chosen_utf = -1;
- if (type == ZFS_TYPE_SNAPSHOT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshot properties cannot be modified"));
- (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
- return (NULL);
- }
-
if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
(void) no_memory(hdl);
return (NULL);
@@ -632,6 +625,13 @@
continue;
}
+ if (type == ZFS_TYPE_SNAPSHOT) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "this property can not be modified for snapshots"));
+ (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
+ goto error;
+ }
+
if (!zfs_prop_valid_for_type(prop, type)) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' does not "
@@ -3450,10 +3450,11 @@
* Takes a snapshot of the given dataset.
*/
int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
+zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
+ nvlist_t *props)
{
const char *delim;
- char *parent;
+ char parent[ZFS_MAXNAMELEN];
zfs_handle_t *zhp;
zfs_cmd_t zc = { 0 };
int ret;
@@ -3466,16 +3467,27 @@
if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+ if (props) {
+ if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
+ props, B_FALSE, NULL, errbuf)) == NULL)
+ return (-1);
+
+ if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
+ nvlist_free(props);
+ return (-1);
+ }
+
+ nvlist_free(props);
+ }
+
/* make sure the parent exists and is of the appropriate type */
delim = strchr(path, '@');
- if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL)
- return (-1);
(void) strncpy(parent, path, delim - path);
parent[delim - path] = '\0';
if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
ZFS_TYPE_VOLUME)) == NULL) {
- free(parent);
+ zcmd_free_nvlists(&zc);
return (-1);
}
@@ -3488,6 +3500,8 @@
zc.zc_cookie = recursive;
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
+ zcmd_free_nvlists(&zc);
+
/*
* if it was recursive, the one that actually failed will be in
* zc.zc_name.
@@ -3510,7 +3524,6 @@
dgettext(TEXT_DOMAIN,
"Volume successfully snapshotted, but device links "
"were not created"));
- free(parent);
zfs_close(zhp);
return (-1);
}
@@ -3519,7 +3532,6 @@
if (ret != 0)
(void) zfs_standard_error(hdl, errno, errbuf);
- free(parent);
zfs_close(zhp);
return (ret);
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c Fri Aug 01 16:32:18 2008 -0700
@@ -166,6 +166,7 @@
uint64_t parent_fromsnap_guid;
nvlist_t *parent_snaps;
nvlist_t *fss;
+ nvlist_t *snapprops;
const char *fromsnap;
const char *tosnap;
@@ -182,6 +183,7 @@
*
* "props" -> { name -> value (only if set here) }
* "snaps" -> { name (lastname) -> number (guid) }
+ * "snapprops" -> { name (lastname) -> { name -> value } }
*
* "origin" -> number (guid) (if clone)
* "sent" -> boolean (not on-disk)
@@ -192,12 +194,15 @@
*/
} send_data_t;
+static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
+
static int
send_iterate_snap(zfs_handle_t *zhp, void *arg)
{
send_data_t *sd = arg;
uint64_t guid = zhp->zfs_dmustats.dds_guid;
char *snapname;
+ nvlist_t *nv;
snapname = strrchr(zhp->zfs_name, '@')+1;
@@ -212,6 +217,11 @@
sd->parent_fromsnap_guid = guid;
}
+ VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
+ send_iterate_prop(zhp, nv);
+ VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
+ nvlist_free(nv);
+
zfs_close(zhp);
return (0);
}
@@ -235,6 +245,8 @@
uint64_t value;
verify(nvlist_lookup_uint64(propnv,
ZPROP_VALUE, &value) == 0);
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+ continue;
} else {
char *source;
if (nvlist_lookup_string(propnv,
@@ -292,9 +304,12 @@
/* iterate over snaps, and set sd->parent_fromsnap_guid */
sd->parent_fromsnap_guid = 0;
VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
+ VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
(void) zfs_iter_snapshots(zhp, send_iterate_snap, sd);
VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
+ VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
nvlist_free(sd->parent_snaps);
+ nvlist_free(sd->snapprops);
/* add this fs to nvlist */
(void) snprintf(guidstring, sizeof (guidstring),
@@ -1180,7 +1195,7 @@
snapelem; snapelem = nextsnapelem) {
uint64_t thisguid;
char *stream_snapname;
- nvlist_t *found;
+ nvlist_t *found, *props;
nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
@@ -1209,6 +1224,22 @@
stream_nvfs = found;
+ if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
+ &props) && 0 == nvlist_lookup_nvlist(props,
+ stream_snapname, &props)) {
+ zfs_cmd_t zc = { 0 };
+
+ zc.zc_cookie = B_TRUE; /* clear current props */
+ snprintf(zc.zc_name, sizeof (zc.zc_name),
+ "%s@%s", fsname, nvpair_name(snapelem));
+ if (zcmd_write_src_nvlist(hdl, &zc,
+ props) == 0) {
+ (void) zfs_ioctl(hdl,
+ ZFS_IOC_SET_PROP, &zc);
+ zcmd_free_nvlists(&zc);
+ }
+ }
+
/* check for different snapname */
if (strcmp(nvpair_name(snapelem),
stream_snapname) != 0) {
@@ -1530,6 +1561,7 @@
boolean_t stream_wantsnewfs;
uint64_t parent_snapguid = 0;
prop_changelist_t *clp = NULL;
+ nvlist_t *snapprops_nvlist = NULL;
begin_time = time(NULL);
@@ -1537,7 +1569,9 @@
"cannot receive"));
if (stream_avl != NULL) {
- nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid, NULL);
+ char *snapname;
+ nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
+ &snapname);
nvlist_t *props;
int ret;
@@ -1555,6 +1589,11 @@
if (err)
nvlist_free(props);
+ if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
+ VERIFY(0 == nvlist_lookup_nvlist(props,
+ snapname, &snapprops_nvlist));
+ }
+
if (ret != 0)
return (-1);
}
@@ -1787,6 +1826,18 @@
err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
ioctl_errno = errno;
+ zcmd_free_nvlists(&zc);
+
+ if (err == 0 && snapprops_nvlist) {
+ zfs_cmd_t zc2 = { 0 };
+
+ strcpy(zc2.zc_name, zc.zc_value);
+ if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) {
+ (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2);
+ zcmd_free_nvlists(&zc2);
+ }
+ }
+
if (err && (ioctl_errno == ENOENT || ioctl_errno == ENODEV)) {
/*
* It may be that this snapshot already exists,
@@ -1822,7 +1873,6 @@
*cp = '@';
}
- zcmd_free_nvlists(&zc);
if (ioctl_err != 0) {
switch (ioctl_errno) {
--- a/usr/src/uts/common/fs/zfs/arc.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/arc.c Fri Aug 01 16:32:18 2008 -0700
@@ -2417,6 +2417,7 @@
uint32_t *arc_flags, const zbookmark_t *zb)
{
int err;
+ arc_buf_hdr_t *hdr = pbuf->b_hdr;
ASSERT(!refcount_is_zero(&pbuf->b_hdr->b_refcnt));
ASSERT3U((char *)bp - (char *)pbuf->b_data, <, pbuf->b_hdr->b_size);
@@ -2425,6 +2426,7 @@
err = arc_read_nolock(pio, spa, bp, done, private, priority,
zio_flags, arc_flags, zb);
+ ASSERT3P(hdr, ==, pbuf->b_hdr);
rw_exit(&pbuf->b_hdr->b_datalock);
return (err);
}
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c Fri Aug 01 16:32:18 2008 -0700
@@ -397,11 +397,11 @@
if (need_lock)
rw_enter(&dp->dp_config_rwlock, RW_READER);
- err = dsl_prop_get_ds_locked(ds->ds_dir,
+ err = dsl_prop_get_ds(ds,
"refreservation", sizeof (uint64_t), 1,
&ds->ds_reserved, NULL);
if (err == 0) {
- err = dsl_prop_get_ds_locked(ds->ds_dir,
+ err = dsl_prop_get_ds(ds,
"refquota", sizeof (uint64_t), 1,
&ds->ds_quota, NULL);
}
@@ -1674,6 +1674,10 @@
VERIFY(0 == dmu_object_free(mos,
ds->ds_phys->ds_next_clones_obj, tx));
}
+ if (ds->ds_phys->ds_props_obj != 0) {
+ VERIFY(0 == zap_destroy(mos,
+ ds->ds_phys->ds_props_obj, tx));
+ }
dsl_dir_close(ds->ds_dir, ds);
ds->ds_dir = NULL;
dsl_dataset_drain_refs(ds, tag);
--- a/usr/src/uts/common/fs/zfs/dsl_deleg.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_deleg.c Fri Aug 01 16:32:18 2008 -0700
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -533,42 +533,33 @@
* Check if user has requested permission.
*/
int
-dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr)
+dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
{
- dsl_dir_t *dd, *startdd;
+ dsl_dataset_t *ds;
+ dsl_dir_t *dd;
dsl_pool_t *dp;
void *cookie;
int error;
char checkflag = ZFS_DELEG_LOCAL;
- const char *tail;
objset_t *mos;
avl_tree_t permsets;
perm_set_t *setnode;
- /*
- * Use tail so that zfs_ioctl() code doesn't have
- * to always to to figure out parent name in order
- * to do access check. for example renaming a snapshot
- */
- error = dsl_dir_open(ddname, FTAG, &startdd, &tail);
+ error = dsl_dataset_hold(dsname, FTAG, &ds);
if (error)
return (error);
- if (tail && tail[0] != '@') {
- dsl_dir_close(startdd, FTAG);
- return (ENOENT);
- }
- dp = startdd->dd_pool;
+ dp = ds->ds_dir->dd_pool;
mos = dp->dp_meta_objset;
if (dsl_delegation_on(mos) == B_FALSE) {
- dsl_dir_close(startdd, FTAG);
+ dsl_dataset_rele(ds, FTAG);
return (ECANCELED);
}
if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
SPA_VERSION_DELEGATED_PERMS) {
- dsl_dir_close(startdd, FTAG);
+ dsl_dataset_rele(ds, FTAG);
return (EPERM);
}
@@ -576,7 +567,7 @@
offsetof(perm_set_t, p_node));
rw_enter(&dp->dp_config_rwlock, RW_READER);
- for (dd = startdd; dd != NULL; dd = dd->dd_parent,
+ for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
checkflag = ZFS_DELEG_DESCENDENT) {
uint64_t zapobj;
boolean_t expanded;
@@ -588,7 +579,7 @@
if (!INGLOBALZONE(curproc)) {
uint64_t zoned;
- if (dsl_prop_get_ds_locked(dd,
+ if (dsl_prop_get_dd(dd,
zfs_prop_to_name(ZFS_PROP_ZONED),
8, 1, &zoned, NULL) != 0)
break;
@@ -637,7 +628,7 @@
error = EPERM;
success:
rw_exit(&dp->dp_config_rwlock);
- dsl_dir_close(startdd, FTAG);
+ dsl_dataset_rele(ds, FTAG);
cookie = NULL;
while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
--- a/usr/src/uts/common/fs/zfs/dsl_prop.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_prop.c Fri Aug 01 16:32:18 2008 -0700
@@ -68,13 +68,16 @@
return (0);
}
-static int
-dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
+int
+dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
int intsz, int numint, void *buf, char *setpoint)
{
int err = ENOENT;
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
zfs_prop_t prop;
+ ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
+
if (setpoint)
setpoint[0] = '\0';
@@ -85,7 +88,6 @@
* ouside this loop.
*/
for (; dd != NULL; dd = dd->dd_parent) {
- objset_t *mos = dd->dd_pool->dp_meta_objset;
ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
propname, intsz, numint, buf);
@@ -107,6 +109,26 @@
return (err);
}
+int
+dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
+ int intsz, int numint, void *buf, char *setpoint)
+{
+ ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
+
+ if (ds->ds_phys->ds_props_obj) {
+ int err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_phys->ds_props_obj, propname, intsz, numint, buf);
+ if (err != ENOENT) {
+ if (setpoint)
+ dsl_dataset_name(ds, setpoint);
+ return (err);
+ }
+ }
+
+ return (dsl_prop_get_dd(ds->ds_dir, propname,
+ intsz, numint, buf, setpoint));
+}
+
/*
* Register interest in the named property. We'll call the callback
* once to notify it of the current property value, and again each time
@@ -119,19 +141,20 @@
dsl_prop_changed_cb_t *callback, void *cbarg)
{
dsl_dir_t *dd = ds->ds_dir;
+ dsl_pool_t *dp = dd->dd_pool;
uint64_t value;
dsl_prop_cb_record_t *cbr;
int err;
int need_rwlock;
- need_rwlock = !RW_WRITE_HELD(&dd->dd_pool->dp_config_rwlock);
+ need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
if (need_rwlock)
- rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
- err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL);
+ err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
if (err != 0) {
if (need_rwlock)
- rw_exit(&dd->dd_pool->dp_config_rwlock);
+ rw_exit(&dp->dp_config_rwlock);
return (err);
}
@@ -147,56 +170,30 @@
cbr->cbr_func(cbr->cbr_arg, value);
- VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object,
+ VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object,
NULL, cbr, &dd));
if (need_rwlock)
- rw_exit(&dd->dd_pool->dp_config_rwlock);
- /* Leave dataset open until this callback is unregistered */
+ rw_exit(&dp->dp_config_rwlock);
+ /* Leave dir open until this callback is unregistered */
return (0);
}
int
-dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
- int intsz, int numints, void *buf, char *setpoint)
-{
- int err;
-
- rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
- err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint);
- rw_exit(&dd->dd_pool->dp_config_rwlock);
-
- return (err);
-}
-
-/*
- * Get property when config lock is already held.
- */
-int dsl_prop_get_ds_locked(dsl_dir_t *dd, const char *propname,
+dsl_prop_get(const char *dsname, const char *propname,
int intsz, int numints, void *buf, char *setpoint)
{
- ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
- return (dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint));
-}
-
-int
-dsl_prop_get(const char *ddname, const char *propname,
- int intsz, int numints, void *buf, char *setpoint)
-{
- dsl_dir_t *dd;
- const char *tail;
+ dsl_dataset_t *ds;
int err;
- err = dsl_dir_open(ddname, FTAG, &dd, &tail);
+ err = dsl_dataset_hold(dsname, FTAG, &ds);
if (err)
return (err);
- if (tail && tail[0] != '@') {
- dsl_dir_close(dd, FTAG);
- return (ENOENT);
- }
- err = dsl_prop_get_dd(dd, propname, intsz, numints, buf, setpoint);
+ rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
+ err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
+ rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
- dsl_dir_close(dd, FTAG);
+ dsl_dataset_rele(ds, FTAG);
return (err);
}
@@ -282,6 +279,7 @@
zap_cursor_t zc;
zap_attribute_t *za;
int err;
+ uint64_t dummyval;
ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
@@ -294,7 +292,7 @@
* being inherited here or below; stop the recursion.
*/
err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
- 8, 1, &value);
+ 8, 1, &dummyval);
if (err == 0) {
dsl_dir_close(dd, FTAG);
return;
@@ -303,11 +301,22 @@
}
mutex_enter(&dd->dd_lock);
- for (cbr = list_head(&dd->dd_prop_cbs);
- cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
- if (strcmp(cbr->cbr_propname, propname) == 0) {
- cbr->cbr_func(cbr->cbr_arg, value);
- }
+ for (cbr = list_head(&dd->dd_prop_cbs); cbr;
+ cbr = list_next(&dd->dd_prop_cbs, cbr)) {
+ uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
+
+ if (strcmp(cbr->cbr_propname, propname) != 0)
+ continue;
+
+ /*
+ * If the property is set on this ds, then it is not
+ * inherited here; don't call the callback.
+ */
+ if (propobj && 0 == zap_lookup(mos, propobj, propname,
+ 8, 1, &dummyval))
+ continue;
+
+ cbr->cbr_func(cbr->cbr_arg, value);
}
mutex_exit(&dd->dd_lock);
@@ -335,22 +344,35 @@
static void
dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
- dsl_dir_t *dd = arg1;
+ dsl_dataset_t *ds = arg1;
struct prop_set_arg *psa = arg2;
- objset_t *mos = dd->dd_pool->dp_meta_objset;
- uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
- uint64_t intval;
+ objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ uint64_t zapobj, intval;
int isint;
char valbuf[32];
char *valstr;
isint = (dodefault(psa->name, 8, 1, &intval) == 0);
+ if (dsl_dataset_is_snapshot(ds)) {
+ ASSERT(spa_version(ds->ds_dir->dd_pool->dp_spa) >=
+ SPA_VERSION_SNAP_PROPS);
+ if (ds->ds_phys->ds_props_obj == 0) {
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ ds->ds_phys->ds_props_obj =
+ zap_create(mos,
+ DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
+ }
+ zapobj = ds->ds_phys->ds_props_obj;
+ } else {
+ zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
+ }
+
if (psa->numints == 0) {
int err = zap_remove(mos, zapobj, psa->name, tx);
ASSERT(err == 0 || err == ENOENT);
if (isint) {
- VERIFY(0 == dsl_prop_get_impl(dd->dd_parent,
+ VERIFY(0 == dsl_prop_get_ds(ds,
psa->name, 8, 1, &intval, NULL));
}
} else {
@@ -361,8 +383,25 @@
}
if (isint) {
- dsl_prop_changed_notify(dd->dd_pool,
- dd->dd_object, psa->name, intval, TRUE);
+ if (dsl_dataset_is_snapshot(ds)) {
+ dsl_prop_cb_record_t *cbr;
+ /*
+ * It's a snapshot; nothing can inherit this
+ * property, so just look for callbacks on this
+ * ds here.
+ */
+ mutex_enter(&ds->ds_dir->dd_lock);
+ for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
+ cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
+ if (cbr->cbr_ds == ds &&
+ strcmp(cbr->cbr_propname, psa->name) == 0)
+ cbr->cbr_func(cbr->cbr_arg, intval);
+ }
+ mutex_exit(&ds->ds_dir->dd_lock);
+ } else {
+ dsl_prop_changed_notify(ds->ds_dir->dd_pool,
+ ds->ds_dir->dd_object, psa->name, intval, TRUE);
+ }
}
if (isint) {
(void) snprintf(valbuf, sizeof (valbuf),
@@ -372,9 +411,8 @@
valstr = (char *)psa->buf;
}
spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT :
- LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr,
- "%s=%s dataset = %llu", psa->name, valstr,
- dd->dd_phys->dd_head_dataset_obj);
+ LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx, cr,
+ "%s=%s dataset = %llu", psa->name, valstr, ds->ds_object);
}
void
@@ -396,27 +434,13 @@
}
int
-dsl_prop_set_dd(dsl_dir_t *dd, const char *propname,
+dsl_prop_set(const char *dsname, const char *propname,
int intsz, int numints, const void *buf)
{
+ dsl_dataset_t *ds;
+ int err;
struct prop_set_arg psa;
- psa.name = propname;
- psa.intsz = intsz;
- psa.numints = numints;
- psa.buf = buf;
-
- return (dsl_sync_task_do(dd->dd_pool,
- NULL, dsl_prop_set_sync, dd, &psa, 2));
-}
-
-int
-dsl_prop_set(const char *ddname, const char *propname,
- int intsz, int numints, const void *buf)
-{
- dsl_dir_t *dd;
- int err;
-
/*
* We must do these checks before we get to the syncfunc, since
* it can't fail.
@@ -426,11 +450,24 @@
if (intsz * numints >= ZAP_MAXVALUELEN)
return (E2BIG);
- err = dsl_dir_open(ddname, FTAG, &dd, NULL);
+ err = dsl_dataset_hold(dsname, FTAG, &ds);
if (err)
return (err);
- err = dsl_prop_set_dd(dd, propname, intsz, numints, buf);
- dsl_dir_close(dd, FTAG);
+
+ if (dsl_dataset_is_snapshot(ds) &&
+ spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_SNAP_PROPS) {
+ dsl_dataset_rele(ds, FTAG);
+ return (ENOTSUP);
+ }
+
+ psa.name = propname;
+ psa.intsz = intsz;
+ psa.numints = numints;
+ psa.buf = buf;
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ NULL, dsl_prop_set_sync, ds, &psa, 2);
+
+ dsl_dataset_rele(ds, FTAG);
return (err);
}
@@ -442,43 +479,51 @@
{
dsl_dataset_t *ds = os->os->os_dsl_dataset;
dsl_dir_t *dd = ds->ds_dir;
- boolean_t snapshot;
+ boolean_t snapshot = dsl_dataset_is_snapshot(ds);
int err = 0;
- dsl_pool_t *dp;
- objset_t *mos;
-
- snapshot = dsl_dataset_is_snapshot(ds);
+ dsl_pool_t *dp = dd->dd_pool;
+ objset_t *mos = dp->dp_meta_objset;
+ uint64_t propobj = ds->ds_phys->ds_props_obj;
VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- dp = dd->dd_pool;
- mos = dp->dp_meta_objset;
+ if (local && snapshot && !propobj)
+ return (0);
rw_enter(&dp->dp_config_rwlock, RW_READER);
- for (; dd != NULL; dd = dd->dd_parent) {
+ while (dd != NULL) {
char setpoint[MAXNAMELEN];
zap_cursor_t zc;
zap_attribute_t za;
+ dsl_dir_t *dd_next;
- dsl_dir_name(dd, setpoint);
+ if (propobj) {
+ dsl_dataset_name(ds, setpoint);
+ dd_next = dd;
+ } else {
+ dsl_dir_name(dd, setpoint);
+ propobj = dd->dd_phys->dd_props_zapobj;
+ dd_next = dd->dd_parent;
+ }
- for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj);
+ for (zap_cursor_init(&zc, mos, propobj);
(err = zap_cursor_retrieve(&zc, &za)) == 0;
zap_cursor_advance(&zc)) {
nvlist_t *propval;
- zfs_prop_t prop;
- /*
- * Skip non-inheritable properties.
- */
- if ((prop = zfs_name_to_prop(za.za_name)) !=
- ZPROP_INVAL && !zfs_prop_inheritable(prop) &&
- dd != ds->ds_dir)
+ zfs_prop_t prop = zfs_name_to_prop(za.za_name);
+
+ /* Skip non-inheritable properties. */
+ if (prop != ZPROP_INVAL &&
+ !zfs_prop_inheritable(prop) &&
+ (dd != ds->ds_dir || (snapshot && dd != dd_next)))
continue;
- if (snapshot &&
+ /* Skip properties not valid for this type. */
+ if (snapshot && prop != ZPROP_INVAL &&
!zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
continue;
+ /* Skip properties already defined */
if (nvlist_lookup_nvlist(*nvp, za.za_name,
&propval) == 0)
continue;
@@ -491,10 +536,8 @@
*/
char *tmp = kmem_alloc(za.za_num_integers,
KM_SLEEP);
- err = zap_lookup(mos,
- dd->dd_phys->dd_props_zapobj,
- za.za_name, 1, za.za_num_integers,
- tmp);
+ err = zap_lookup(mos, propobj,
+ za.za_name, 1, za.za_num_integers, tmp);
if (err != 0) {
kmem_free(tmp, za.za_num_integers);
break;
@@ -528,6 +571,8 @@
*/
if (local)
break;
+ dd = dd_next;
+ propobj = 0;
}
rw_exit(&dp->dp_config_rwlock);
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h Fri Aug 01 16:32:18 2008 -0700
@@ -93,8 +93,9 @@
uint64_t ds_guid;
uint64_t ds_flags; /* DS_FLAG_* */
blkptr_t ds_bp;
- uint64_t ds_next_clones_obj; /* DMU_OT_DSL_NEXT_CLONES */
- uint64_t ds_pad[7]; /* pad out to 320 bytes for good measure */
+ uint64_t ds_next_clones_obj; /* DMU_OT_DSL_CLONES */
+ uint64_t ds_props_obj; /* DMU_OT_DSL_PROPS for snaps */
+ uint64_t ds_pad[6]; /* pad out to 320 bytes for good measure */
} dsl_dataset_phys_t;
typedef struct dsl_dataset {
--- a/usr/src/uts/common/fs/zfs/sys/dsl_prop.h Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_prop.h Fri Aug 01 16:32:18 2008 -0700
@@ -37,6 +37,7 @@
#endif
struct dsl_dataset;
+struct dsl_dir;
/* The callback func may not call into the DMU or DSL! */
typedef void (dsl_prop_changed_cb_t)(void *arg, uint64_t newval);
@@ -55,20 +56,18 @@
dsl_prop_changed_cb_t *callback, void *cbarg);
int dsl_prop_numcb(struct dsl_dataset *ds);
-int dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
- int intsz, int numints, void *buf, char *setpoint);
int dsl_prop_get(const char *ddname, const char *propname,
int intsz, int numints, void *buf, char *setpoint);
int dsl_prop_get_integer(const char *ddname, const char *propname,
uint64_t *valuep, char *setpoint);
int dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local);
-int dsl_prop_get_ds_locked(dsl_dir_t *dd, const char *propname,
+int dsl_prop_get_ds(struct dsl_dataset *ds, const char *propname,
+ int intsz, int numints, void *buf, char *setpoint);
+int dsl_prop_get_dd(struct dsl_dir *dd, const char *propname,
int intsz, int numints, void *buf, char *setpoint);
int dsl_prop_set(const char *ddname, const char *propname,
int intsz, int numints, const void *buf);
-int dsl_prop_set_dd(dsl_dir_t *dd, const char *propname,
- int intsz, int numints, const void *buf);
void dsl_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
cred_t *cr, dmu_tx_t *tx);
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Fri Aug 01 16:32:18 2008 -0700
@@ -92,6 +92,7 @@
boolean_t zvec_his_log;
} zfs_ioc_vec_t;
+static void clear_props(char *dataset, nvlist_t *props);
static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
boolean_t *);
int zfs_set_prop_nvlist(const char *, nvlist_t *);
@@ -1465,8 +1466,6 @@
return (ENOTSUP);
break;
}
- if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0)
- return (error);
}
elem = NULL;
@@ -1579,6 +1578,7 @@
* zc_name name of filesystem
* zc_value name of property to inherit
* zc_nvlist_src{_size} nvlist of properties to apply
+ * zc_cookie clear existing local props?
*
* outputs: none
*/
@@ -1592,6 +1592,21 @@
&nvl)) != 0)
return (error);
+ if (zc->zc_cookie) {
+ nvlist_t *origprops;
+ objset_t *os;
+
+ if (dmu_objset_open(zc->zc_name, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &os) == 0) {
+ if (dsl_prop_get_all(os, &origprops, TRUE) == 0) {
+ clear_props(zc->zc_name, origprops);
+ nvlist_free(origprops);
+ }
+ dmu_objset_close(os);
+ }
+
+ }
+
error = zfs_set_prop_nvlist(zc->zc_name, nvl);
nvlist_free(nvl);
@@ -2129,6 +2144,27 @@
return (error);
}
+struct snap_prop_arg {
+ nvlist_t *nvprops;
+ const char *snapname;
+};
+
+static int
+set_snap_props(char *name, void *arg)
+{
+ struct snap_prop_arg *snpa = arg;
+ int len = strlen(name) + strlen(snpa->snapname) + 2;
+ char *buf = kmem_alloc(len, KM_SLEEP);
+ int err;
+
+ (void) snprintf(buf, len, "%s@%s", name, snpa->snapname);
+ err = zfs_set_prop_nvlist(buf, snpa->nvprops);
+ if (err)
+ (void) dmu_objset_destroy(buf);
+ kmem_free(buf, len);
+ return (err);
+}
+
/*
* inputs:
* zc_name name of filesystem
@@ -2140,10 +2176,40 @@
static int
zfs_ioc_snapshot(zfs_cmd_t *zc)
{
+ nvlist_t *nvprops = NULL;
+ int error;
+ boolean_t recursive = zc->zc_cookie;
+
if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
return (EINVAL);
- return (dmu_objset_snapshot(zc->zc_name,
- zc->zc_value, zc->zc_cookie));
+
+ if (zc->zc_nvlist_src != NULL &&
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &nvprops)) != 0)
+ return (error);
+
+ error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, recursive);
+
+ /*
+ * It would be nice to do this atomically.
+ */
+ if (error == 0) {
+ struct snap_prop_arg snpa;
+ snpa.nvprops = nvprops;
+ snpa.snapname = zc->zc_value;
+ if (recursive) {
+ error = dmu_objset_find(zc->zc_name,
+ set_snap_props, &snpa, DS_FIND_CHILDREN);
+ if (error) {
+ (void) dmu_snapshots_destroy(zc->zc_name,
+ zc->zc_value);
+ }
+ } else {
+ error = set_snap_props(zc->zc_name, &snpa);
+ }
+ }
+ nvlist_free(nvprops);
+ return (error);
}
int
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c Fri Aug 01 16:32:18 2008 -0700
@@ -477,8 +477,9 @@
dmu_objset_name(os, osname);
if (error = dsl_prop_get_integer(osname, "nbmand", &nbmand,
- NULL))
- return (error);
+ NULL)) {
+ return (error);
+ }
}
/*
--- a/usr/src/uts/common/fs/zfs/zvol.c Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zvol.c Fri Aug 01 16:32:18 2008 -0700
@@ -843,7 +843,11 @@
error = dmu_free_long_range(zv->zv_objset,
ZVOL_OBJ, volsize, DMU_OBJECT_END);
- if (error == 0) {
+ /*
+ * If we are using a faked-up state (zv_minor == 0) then don't
+ * try to update the in-core zvol state.
+ */
+ if (error == 0 && zv->zv_minor) {
zv->zv_volsize = volsize;
zvol_size_changed(zv, maj);
}
@@ -857,25 +861,31 @@
int error;
dmu_object_info_t doi;
uint64_t old_volsize = 0ULL;
+ zvol_state_t state = { 0 };
mutex_enter(&zvol_state_lock);
if ((zv = zvol_minor_lookup(name)) == NULL) {
- mutex_exit(&zvol_state_lock);
- return (ENXIO);
+ /*
+ * If we are doing a "zfs clone -o volsize=", then the
+ * minor node won't exist yet.
+ */
+ error = dmu_objset_open(name, DMU_OST_ZVOL, DS_MODE_OWNER,
+ &state.zv_objset);
+ if (error != 0)
+ goto out;
+ zv = &state;
}
old_volsize = zv->zv_volsize;
if ((error = dmu_object_info(zv->zv_objset, ZVOL_OBJ, &doi)) != 0 ||
(error = zvol_check_volsize(volsize,
- doi.doi_data_block_size)) != 0) {
- mutex_exit(&zvol_state_lock);
- return (error);
- }
+ doi.doi_data_block_size)) != 0)
+ goto out;
if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY)) {
- mutex_exit(&zvol_state_lock);
- return (EROFS);
+ error = EROFS;
+ goto out;
}
error = zvol_update_volsize(zv, maj, volsize);
@@ -893,6 +903,10 @@
}
}
+out:
+ if (state.zv_objset)
+ dmu_objset_close(state.zv_objset);
+
mutex_exit(&zvol_state_lock);
return (error);
--- a/usr/src/uts/common/sys/fs/zfs.h Fri Aug 01 16:15:10 2008 -0700
+++ b/usr/src/uts/common/sys/fs/zfs.h Fri Aug 01 16:32:18 2008 -0700
@@ -244,13 +244,14 @@
#define SPA_VERSION_9 9ULL
#define SPA_VERSION_10 10ULL
#define SPA_VERSION_11 11ULL
+#define SPA_VERSION_12 12ULL
/*
* When bumping up SPA_VERSION, make sure GRUB ZFS understands the on-disk
* format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*},
* and do the appropriate changes.
*/
-#define SPA_VERSION SPA_VERSION_11
-#define SPA_VERSION_STRING "11"
+#define SPA_VERSION SPA_VERSION_12
+#define SPA_VERSION_STRING "12"
/*
* Symbolic names for the changes that caused a SPA_VERSION switch.
@@ -283,6 +284,7 @@
#define SPA_VERSION_NEXT_CLONES SPA_VERSION_11
#define SPA_VERSION_ORIGIN SPA_VERSION_11
#define SPA_VERSION_DSL_SCRUB SPA_VERSION_11
+#define SPA_VERSION_SNAP_PROPS SPA_VERSION_12
/*
* ZPL version - rev'd whenever an incompatible on-disk format change