/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
* Copyright (c) 2013 Martin Matuska. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc.
+ * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
*/
#include <ctype.h>
return (dgettext(TEXT_DOMAIN, "snapshot"));
case ZFS_TYPE_VOLUME:
return (dgettext(TEXT_DOMAIN, "volume"));
+ case ZFS_TYPE_POOL:
+ return (dgettext(TEXT_DOMAIN, "pool"));
+ case ZFS_TYPE_BOOKMARK:
+ return (dgettext(TEXT_DOMAIN, "bookmark"));
default:
- break;
+ assert(!"unhandled zfs_type_t");
}
return (NULL);
char what;
(void) zfs_prop_get_table();
- if (dataset_namecheck(path, &why, &what) != 0) {
+ if (entity_namecheck(path, &why, &what) != 0) {
if (hdl != NULL) {
switch (why) {
case NAME_ERR_TOOLONG:
"'%c' in name"), what);
break;
- case NAME_ERR_MULTIPLE_AT:
+ case NAME_ERR_MULTIPLE_DELIMITERS:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "multiple '@' delimiters in name"));
+ "multiple '@' and/or '#' delimiters in "
+ "name"));
break;
case NAME_ERR_NOLETTER:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"reserved disk name"));
break;
+
default:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "(%d) not defined"), why);
break;
}
}
if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
if (hdl != NULL)
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshot delimiter '@' in filesystem name"));
+ "snapshot delimiter '@' is not expected here"));
return (0);
}
return (0);
}
+ if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) {
+ if (hdl != NULL)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "bookmark delimiter '#' is not expected here"));
+ return (0);
+ }
+
+ if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) {
+ if (hdl != NULL)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "missing '#' delimiter in bookmark name"));
+ return (0);
+ }
+
if (modifying && strchr(path, '%') != NULL) {
if (hdl != NULL)
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
return (zhp);
}
+struct zfs_open_bookmarks_cb_data {
+ const char *path;
+ zfs_handle_t *zhp;
+};
+
+static int
+zfs_open_bookmarks_cb(zfs_handle_t *zhp, void *data)
+{
+ struct zfs_open_bookmarks_cb_data *dp = data;
+
+ /*
+ * Is it the one we are looking for?
+ */
+ if (strcmp(dp->path, zfs_get_name(zhp)) == 0) {
+ /*
+ * We found it. Save it and let the caller know we are done.
+ */
+ dp->zhp = zhp;
+ return (EEXIST);
+ }
+
+ /*
+ * Not found. Close the handle and ask for another one.
+ */
+ zfs_close(zhp);
+ return (0);
+}
+
/*
- * Opens the given snapshot, filesystem, or volume. The 'types'
+ * Opens the given snapshot, bookmark, filesystem, or volume. The 'types'
* argument is a mask of acceptable types. The function will print an
* appropriate error message and return NULL if it can't be opened.
*/
{
zfs_handle_t *zhp;
char errbuf[1024];
+ char *bookp;
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
/*
* Validate the name before we even try to open it.
*/
- if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid dataset name"));
+ if (!zfs_validate_name(hdl, path, types, B_FALSE)) {
(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
return (NULL);
}
/*
- * Try to get stats for the dataset, which will tell us if it exists.
+ * Bookmarks needs to be handled separately.
*/
- errno = 0;
- if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- return (NULL);
+ bookp = strchr(path, '#');
+ if (bookp == NULL) {
+ /*
+ * Try to get stats for the dataset, which will tell us if it
+ * exists.
+ */
+ errno = 0;
+ if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
+ (void) zfs_standard_error(hdl, errno, errbuf);
+ return (NULL);
+ }
+ } else {
+ char dsname[ZFS_MAX_DATASET_NAME_LEN];
+ zfs_handle_t *pzhp;
+ struct zfs_open_bookmarks_cb_data cb_data = {path, NULL};
+
+ /*
+ * We need to cut out '#' and everything after '#'
+ * to get the parent dataset name only.
+ */
+ assert(bookp - path < sizeof (dsname));
+ (void) strncpy(dsname, path, bookp - path);
+ dsname[bookp - path] = '\0';
+
+ /*
+ * Create handle for the parent dataset.
+ */
+ errno = 0;
+ if ((pzhp = make_dataset_handle(hdl, dsname)) == NULL) {
+ (void) zfs_standard_error(hdl, errno, errbuf);
+ return (NULL);
+ }
+
+ /*
+ * Iterate bookmarks to find the right one.
+ */
+ errno = 0;
+ if ((zfs_iter_bookmarks(pzhp, zfs_open_bookmarks_cb,
+ &cb_data) == 0) && (cb_data.zhp == NULL)) {
+ (void) zfs_error(hdl, EZFS_NOENT, errbuf);
+ zfs_close(pzhp);
+ return (NULL);
+ }
+ if (cb_data.zhp == NULL) {
+ (void) zfs_standard_error(hdl, errno, errbuf);
+ zfs_close(pzhp);
+ return (NULL);
+ }
+ zhp = cb_data.zhp;
+
+ /*
+ * Cleanup.
+ */
+ zfs_close(pzhp);
}
if (!(types & zhp->zfs_type)) {
void *cookie = NULL;
mnttab_node_t *mtn;
- while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))) {
+ while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))
+ != NULL) {
free(mtn->mtn_mt.mnt_special);
free(mtn->mtn_mt.mnt_mountp);
free(mtn->mtn_mt.mnt_fstype);
mnttab_node_t *ret;
find.mtn_mt.mnt_special = (char *)fsname;
- if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))) {
+ if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))
+ != NULL) {
avl_remove(&hdl->libzfs_mnttab_cache, ret);
free(ret->mtn_mt.mnt_special);
free(ret->mtn_mt.mnt_mountp);
*/
if (intval < SPA_MINBLOCKSIZE ||
intval > maxbs || !ISP2(intval)) {
- zfs_nicenum(maxbs, buf, sizeof (buf));
+ zfs_nicebytes(maxbs, buf, sizeof (buf));
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'%s' must be power of 2 from 512B "
"to %s"), propname, buf);
"component of '%s' is too long"),
propname);
break;
+
default:
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "(%d) not defined"),
+ why);
break;
}
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
}
break;
+
case ZFS_PROP_UTF8ONLY:
chosen_utf = (int)intval;
break;
+
case ZFS_PROP_NORMALIZE:
chosen_normal = (int)intval;
break;
+
default:
break;
}
case ZFS_PROP_VOLSIZE:
if (intval % blocksize != 0) {
- zfs_nicenum(blocksize, buf,
+ zfs_nicebytes(blocksize, buf,
sizeof (buf));
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'%s' must be a multiple of "
goto error;
}
break;
+
default:
break;
}
* its canmount property to 'on' or 'noauto'. We only use
* the changelist logic to unmount when setting canmount=off.
*/
- if (!(prop == ZFS_PROP_CANMOUNT &&
- fnvpair_value_uint64(elem) != ZFS_CANMOUNT_OFF)) {
+ if (prop != ZFS_PROP_CANMOUNT ||
+ (fnvpair_value_uint64(elem) == ZFS_CANMOUNT_OFF &&
+ zfs_is_mounted(zhp, NULL))) {
cls[cl_idx] = changelist_gather(zhp, prop, 0, 0);
if (cls[cl_idx] == NULL)
goto error;
mntopt_on = MNTOPT_NBMAND;
mntopt_off = MNTOPT_NONBMAND;
break;
+
default:
break;
}
/*
* If we tried to use a default value for a
* readonly property, it means that it was not
- * present.
+ * present. Note this only applies to "truly"
+ * readonly properties, not set-once properties
+ * like volblocksize.
*/
if (zfs_prop_readonly(prop) &&
+ !zfs_prop_setonce(prop) &&
*source != NULL && (*source)[0] == '\0') {
*source = NULL;
+ return (-1);
}
break;
(void) snprintf(propbuf, proplen, "%llu",
(u_longlong_t)val);
else
- zfs_nicenum(val, propbuf, proplen);
+ zfs_nicebytes(val, propbuf, proplen);
}
break;
break;
case ZFS_PROP_GUID:
+ case ZFS_PROP_CREATETXG:
/*
* GUIDs are stored as numbers, but they are identifiers.
* We don't want them to be pretty printed, because pretty
(void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
break;
+ case ZFS_PROP_REFERENCED:
+ case ZFS_PROP_AVAILABLE:
+ case ZFS_PROP_USED:
+ case ZFS_PROP_USEDSNAP:
+ case ZFS_PROP_USEDDS:
+ case ZFS_PROP_USEDREFRESERV:
+ case ZFS_PROP_USEDCHILD:
+ if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
+ return (-1);
+ if (literal)
+ (void) snprintf(propbuf, proplen, "%llu",
+ (u_longlong_t)val);
+ else
+ zfs_nicebytes(val, propbuf, proplen);
+ break;
+
default:
switch (zfs_prop_get_type(prop)) {
case PROP_TYPE_NUMBER:
(type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA)) {
(void) strlcpy(propbuf, "none", proplen);
+ } else if (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
+ type == ZFS_PROP_USERUSED || type == ZFS_PROP_GROUPUSED) {
+ zfs_nicebytes(propvalue, propbuf, proplen);
} else {
zfs_nicenum(propvalue, propbuf, proplen);
}
(void) snprintf(propbuf, proplen, "%llu",
(u_longlong_t)propvalue);
} else {
- zfs_nicenum(propvalue, propbuf, proplen);
+ zfs_nicebytes(propvalue, propbuf, proplen);
}
return (0);
return (zhp->zfs_name);
}
+/*
+ * Returns the name of the parent pool for the given zfs handle.
+ */
+const char *
+zfs_get_pool_name(const zfs_handle_t *zhp)
+{
+ return (zhp->zpool_hdl->zpool_name);
+}
+
/*
* Returns the type of the given zfs handle.
*/
* up to the prefixlen-long one.
*/
for (cp = target + prefixlen + 1;
- (cp = strchr(cp, '/')); *cp = '/', cp++) {
+ (cp = strchr(cp, '/')) != NULL; *cp = '/', cp++) {
*cp = '\0';
uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
char errbuf[1024];
uint64_t zoned;
- dmu_objset_type_t ost;
+ enum lzc_dataset_type ost;
+ zpool_handle_t *zpool_handle;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), path);
}
if (type == ZFS_TYPE_VOLUME)
- ost = DMU_OST_ZVOL;
+ ost = LZC_DATSET_TYPE_ZVOL;
else
- ost = DMU_OST_ZFS;
+ ost = LZC_DATSET_TYPE_ZFS;
/* open zpool handle for prop validation */
char pool_path[ZFS_MAX_DATASET_NAME_LEN];
if (p != NULL)
*p = '\0';
- zpool_handle_t *zpool_handle = zpool_open(hdl, pool_path);
+ if ((zpool_handle = zpool_open(hdl, pool_path)) == NULL)
+ return (-1);
if (props && (props = zfs_valid_proplist(hdl, type, props,
zoned, NULL, zpool_handle, errbuf)) == 0) {
zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
boolean_t force_unmount)
{
- int ret;
+ int ret = 0;
zfs_cmd_t zc = {"\0"};
char *delim;
prop_changelist_t *cl = NULL;