Skip to content

Commit

Permalink
OpenZFS 7386 - zfs get does not work properly with bookmarks
Browse files Browse the repository at this point in the history
Authored by: Marcel Telka <[email protected]>
Reviewed by: - Simon Klinkert <[email protected]>
Reviewed by: - Paul Dagnelie <[email protected]>
Approved by: - Matthew Ahrens <[email protected]>

Ported-by: George Melikov <[email protected]>

OpenZFS-issue: https://www.illumos.org/issues/7386
OpenZFS-commit: openzfs/openzfs@edb901a
  • Loading branch information
mtelka authored and gmelikov committed Jan 26, 2017
1 parent 935550f commit fd5ad54
Show file tree
Hide file tree
Showing 13 changed files with 306 additions and 69 deletions.
4 changes: 2 additions & 2 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ get_usage(zfs_help_t idx)
"[-o \"all\" | field[,...]]\n"
"\t [-t type[,...]] [-s source[,...]]\n"
"\t <\"all\" | property[,...]> "
"[filesystem|volume|snapshot] ...\n"));
"[filesystem|volume|snapshot|bookmark] ...\n"));
case HELP_INHERIT:
return (gettext("\tinherit [-rS] <property> "
"<filesystem|volume|snapshot> ...\n"));
Expand Down Expand Up @@ -1608,7 +1608,7 @@ zfs_do_get(int argc, char **argv)
{
zprop_get_cbdata_t cb = { 0 };
int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
int types = ZFS_TYPE_DATASET;
int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;
char *value, *fields;
int ret = 0;
int limit = 0;
Expand Down
3 changes: 2 additions & 1 deletion include/zfs_namecheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ typedef enum {
NAME_ERR_EMPTY_COMPONENT, /* name contains an empty component */
NAME_ERR_TRAILING_SLASH, /* name ends with a slash */
NAME_ERR_INVALCHAR, /* invalid character found */
NAME_ERR_MULTIPLE_AT, /* multiple '@' characters found */
NAME_ERR_MULTIPLE_DELIMITERS, /* multiple '@'/'#' delimiters found */
NAME_ERR_NOLETTER, /* pool doesn't begin with a letter */
NAME_ERR_RESERVED, /* entire name is reserved */
NAME_ERR_DISKLIKE, /* reserved disk name (c[0-9].*) */
Expand All @@ -49,6 +49,7 @@ typedef enum {
#define ZFS_PERMSET_MAXLEN 64

int pool_namecheck(const char *, namecheck_err_t *, char *);
int entity_namecheck(const char *, namecheck_err_t *, char *);
int dataset_namecheck(const char *, namecheck_err_t *, char *);
int mountpoint_namecheck(const char *, namecheck_err_t *);
int zfs_component_namecheck(const char *, namecheck_err_t *, char *);
Expand Down
118 changes: 105 additions & 13 deletions lib/libzfs/libzfs_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
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:
Expand Down Expand Up @@ -129,9 +129,10 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
"'%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:
Expand Down Expand Up @@ -159,7 +160,7 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
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);
}

Expand All @@ -170,6 +171,20 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
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,
Expand Down Expand Up @@ -610,8 +625,36 @@ make_bookmark_handle(zfs_handle_t *parent, const char *path,
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.
*/
Expand All @@ -620,27 +663,76 @@ zfs_open(libzfs_handle_t *hdl, const char *path, int types)
{
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)) {
Expand Down
5 changes: 3 additions & 2 deletions lib/libzfs/libzfs_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -1031,9 +1031,10 @@ zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
"trailing slash in name"));
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_NO_AT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
Expand Down
2 changes: 1 addition & 1 deletion lib/libzfs/libzfs_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ zfs_get_pool_handle(const zfs_handle_t *zhp)
* Given a name, determine whether or not it's a valid path
* (starts with '/' or "./"). If so, walk the mnttab trying
* to match the device number. If not, treat the path as an
* fs/vol/snap name.
* fs/vol/snap/bkmark name.
*/
zfs_handle_t *
zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
Expand Down
4 changes: 2 additions & 2 deletions man/man8/zfs.8
Original file line number Diff line number Diff line change
Expand Up @@ -2202,7 +2202,7 @@ A comma-separated list of types to display, where \fItype\fR is one of \fBfilesy
.ne 2
.na
\fB\fBzfs set\fR \fIproperty\fR=\fIvalue\fR[ \fIproperty\fR=\fIvalue\fR]...
\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR
\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR|\fIbookmark\fR ...\fR
.ad
.sp .6
.RS 4n
Expand All @@ -2214,7 +2214,7 @@ can be set on snapshots. For more information, see the "User Properties" section

.sp
.ne 2
\fB\fBzfs get\fR [\fB-r\fR|\fB-d\fR \fIdepth\fR] [\fB-Hp\fR] [\fB-o\fR \fIfield\fR[,...] [\fB-t\fR \fItype\fR[,...]] [\fB-s\fR \fIsource\fR[,...] "\fIall\fR" | \fIproperty\fR[,...] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR
\fB\fBzfs get\fR [\fB-r\fR|\fB-d\fR \fIdepth\fR] [\fB-Hp\fR] [\fB-o\fR \fIfield\fR[,...] [\fB-t\fR \fItype\fR[,...]] [\fB-s\fR \fIsource\fR[,...] "\fIall\fR" | \fIproperty\fR[,...] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR|\fIbookmark\fR ...\fR
.ad
.sp .6
.RS 4n
Expand Down
Loading

0 comments on commit fd5ad54

Please sign in to comment.