mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-25 02:49:32 +03:00
OpenZFS 7386 - zfs get does not work properly with bookmarks
Authored by: Marcel Telka <marcel@telka.sk> Reviewed by: Simon Klinkert <simon.klinkert@gmail.com> Reviewed by: Paul Dagnelie <pcd@delphix.com> Approved by: Matthew Ahrens <mahrens@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Ported-by: George Melikov <mail@gmelikov.ru> OpenZFS-issue: https://www.illumos.org/issues/7386 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/edb901a Closes #5666
This commit is contained in:
parent
0a3d2673de
commit
aeacdefedc
@ -232,7 +232,7 @@ get_usage(zfs_help_t idx)
|
|||||||
"[-o \"all\" | field[,...]]\n"
|
"[-o \"all\" | field[,...]]\n"
|
||||||
"\t [-t type[,...]] [-s source[,...]]\n"
|
"\t [-t type[,...]] [-s source[,...]]\n"
|
||||||
"\t <\"all\" | property[,...]> "
|
"\t <\"all\" | property[,...]> "
|
||||||
"[filesystem|volume|snapshot] ...\n"));
|
"[filesystem|volume|snapshot|bookmark] ...\n"));
|
||||||
case HELP_INHERIT:
|
case HELP_INHERIT:
|
||||||
return (gettext("\tinherit [-rS] <property> "
|
return (gettext("\tinherit [-rS] <property> "
|
||||||
"<filesystem|volume|snapshot> ...\n"));
|
"<filesystem|volume|snapshot> ...\n"));
|
||||||
@ -1608,7 +1608,7 @@ zfs_do_get(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
zprop_get_cbdata_t cb = { 0 };
|
zprop_get_cbdata_t cb = { 0 };
|
||||||
int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
|
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;
|
char *value, *fields;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int limit = 0;
|
int limit = 0;
|
||||||
|
@ -38,7 +38,7 @@ typedef enum {
|
|||||||
NAME_ERR_EMPTY_COMPONENT, /* name contains an empty component */
|
NAME_ERR_EMPTY_COMPONENT, /* name contains an empty component */
|
||||||
NAME_ERR_TRAILING_SLASH, /* name ends with a slash */
|
NAME_ERR_TRAILING_SLASH, /* name ends with a slash */
|
||||||
NAME_ERR_INVALCHAR, /* invalid character found */
|
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_NOLETTER, /* pool doesn't begin with a letter */
|
||||||
NAME_ERR_RESERVED, /* entire name is reserved */
|
NAME_ERR_RESERVED, /* entire name is reserved */
|
||||||
NAME_ERR_DISKLIKE, /* reserved disk name (c[0-9].*) */
|
NAME_ERR_DISKLIKE, /* reserved disk name (c[0-9].*) */
|
||||||
@ -49,6 +49,7 @@ typedef enum {
|
|||||||
#define ZFS_PERMSET_MAXLEN 64
|
#define ZFS_PERMSET_MAXLEN 64
|
||||||
|
|
||||||
int pool_namecheck(const char *, namecheck_err_t *, char *);
|
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 dataset_namecheck(const char *, namecheck_err_t *, char *);
|
||||||
int mountpoint_namecheck(const char *, namecheck_err_t *);
|
int mountpoint_namecheck(const char *, namecheck_err_t *);
|
||||||
int zfs_component_namecheck(const char *, namecheck_err_t *, char *);
|
int zfs_component_namecheck(const char *, namecheck_err_t *, char *);
|
||||||
|
@ -100,7 +100,7 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
|
|||||||
char what;
|
char what;
|
||||||
|
|
||||||
(void) zfs_prop_get_table();
|
(void) zfs_prop_get_table();
|
||||||
if (dataset_namecheck(path, &why, &what) != 0) {
|
if (entity_namecheck(path, &why, &what) != 0) {
|
||||||
if (hdl != NULL) {
|
if (hdl != NULL) {
|
||||||
switch (why) {
|
switch (why) {
|
||||||
case NAME_ERR_TOOLONG:
|
case NAME_ERR_TOOLONG:
|
||||||
@ -129,9 +129,10 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
|
|||||||
"'%c' in name"), what);
|
"'%c' in name"), what);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NAME_ERR_MULTIPLE_AT:
|
case NAME_ERR_MULTIPLE_DELIMITERS:
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
"multiple '@' delimiters in name"));
|
"multiple '@' and/or '#' delimiters in "
|
||||||
|
"name"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NAME_ERR_NOLETTER:
|
case NAME_ERR_NOLETTER:
|
||||||
@ -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 (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
|
||||||
if (hdl != NULL)
|
if (hdl != NULL)
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
"snapshot delimiter '@' in filesystem name"));
|
"snapshot delimiter '@' is not expected here"));
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +171,20 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
|
|||||||
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 (modifying && strchr(path, '%') != NULL) {
|
||||||
if (hdl != NULL)
|
if (hdl != NULL)
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
@ -610,8 +625,36 @@ make_bookmark_handle(zfs_handle_t *parent, const char *path,
|
|||||||
return (zhp);
|
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
|
* argument is a mask of acceptable types. The function will print an
|
||||||
* appropriate error message and return NULL if it can't be opened.
|
* appropriate error message and return NULL if it can't be opened.
|
||||||
*/
|
*/
|
||||||
@ -620,6 +663,7 @@ zfs_open(libzfs_handle_t *hdl, const char *path, int types)
|
|||||||
{
|
{
|
||||||
zfs_handle_t *zhp;
|
zfs_handle_t *zhp;
|
||||||
char errbuf[1024];
|
char errbuf[1024];
|
||||||
|
char *bookp;
|
||||||
|
|
||||||
(void) snprintf(errbuf, sizeof (errbuf),
|
(void) snprintf(errbuf, sizeof (errbuf),
|
||||||
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
|
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
|
||||||
@ -627,20 +671,68 @@ zfs_open(libzfs_handle_t *hdl, const char *path, int types)
|
|||||||
/*
|
/*
|
||||||
* Validate the name before we even try to open it.
|
* Validate the name before we even try to open it.
|
||||||
*/
|
*/
|
||||||
if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
|
if (!zfs_validate_name(hdl, path, types, B_FALSE)) {
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"invalid dataset name"));
|
|
||||||
(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to get stats for the dataset, which will tell us if it exists.
|
* Bookmarks needs to be handled separately.
|
||||||
*/
|
*/
|
||||||
errno = 0;
|
bookp = strchr(path, '#');
|
||||||
if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
|
if (bookp == NULL) {
|
||||||
(void) zfs_standard_error(hdl, errno, errbuf);
|
/*
|
||||||
return (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)) {
|
if (!(types & zhp->zfs_type)) {
|
||||||
|
@ -1031,9 +1031,10 @@ zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
|
|||||||
"trailing slash in name"));
|
"trailing slash in name"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NAME_ERR_MULTIPLE_AT:
|
case NAME_ERR_MULTIPLE_DELIMITERS:
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
"multiple '@' delimiters in name"));
|
"multiple '@' and/or '#' delimiters in "
|
||||||
|
"name"));
|
||||||
break;
|
break;
|
||||||
case NAME_ERR_NO_AT:
|
case NAME_ERR_NO_AT:
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
@ -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
|
* Given a name, determine whether or not it's a valid path
|
||||||
* (starts with '/' or "./"). If so, walk the mnttab trying
|
* (starts with '/' or "./"). If so, walk the mnttab trying
|
||||||
* to match the device number. If not, treat the path as an
|
* 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_handle_t *
|
||||||
zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
|
zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
|
||||||
|
@ -2202,7 +2202,7 @@ A comma-separated list of types to display, where \fItype\fR is one of \fBfilesy
|
|||||||
.ne 2
|
.ne 2
|
||||||
.na
|
.na
|
||||||
\fB\fBzfs set\fR \fIproperty\fR=\fIvalue\fR[ \fIproperty\fR=\fIvalue\fR]...
|
\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
|
.ad
|
||||||
.sp .6
|
.sp .6
|
||||||
.RS 4n
|
.RS 4n
|
||||||
@ -2214,7 +2214,7 @@ can be set on snapshots. For more information, see the "User Properties" section
|
|||||||
|
|
||||||
.sp
|
.sp
|
||||||
.ne 2
|
.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
|
.ad
|
||||||
.sp .6
|
.sp .6
|
||||||
.RS 4n
|
.RS 4n
|
||||||
|
@ -120,9 +120,9 @@ permset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dataset names must be of the following form:
|
* Entity names must be of the following form:
|
||||||
*
|
*
|
||||||
* [component][/]*[component][@component]
|
* [component/]*[component][(@|#)component]?
|
||||||
*
|
*
|
||||||
* Where each component is made up of alphanumeric characters plus the following
|
* Where each component is made up of alphanumeric characters plus the following
|
||||||
* characters:
|
* characters:
|
||||||
@ -133,10 +133,10 @@ permset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
|||||||
* names for temporary clones (for online recv).
|
* names for temporary clones (for online recv).
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
entity_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||||
{
|
{
|
||||||
const char *loc, *end;
|
const char *start, *end, *loc;
|
||||||
int found_snapshot;
|
int found_delim;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the name is not too long.
|
* Make sure the name is not too long.
|
||||||
@ -160,12 +160,13 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
loc = path;
|
start = path;
|
||||||
found_snapshot = 0;
|
found_delim = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Find the end of this component */
|
/* Find the end of this component */
|
||||||
end = loc;
|
end = start;
|
||||||
while (*end != '/' && *end != '@' && *end != '\0')
|
while (*end != '/' && *end != '@' && *end != '#' &&
|
||||||
|
*end != '\0')
|
||||||
end++;
|
end++;
|
||||||
|
|
||||||
if (*end == '\0' && end[-1] == '/') {
|
if (*end == '\0' && end[-1] == '/') {
|
||||||
@ -175,25 +176,8 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zero-length components are not allowed */
|
|
||||||
if (loc == end) {
|
|
||||||
if (why) {
|
|
||||||
/*
|
|
||||||
* Make sure this is really a zero-length
|
|
||||||
* component and not a '@@'.
|
|
||||||
*/
|
|
||||||
if (*end == '@' && found_snapshot) {
|
|
||||||
*why = NAME_ERR_MULTIPLE_AT;
|
|
||||||
} else {
|
|
||||||
*why = NAME_ERR_EMPTY_COMPONENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate the contents of this component */
|
/* Validate the contents of this component */
|
||||||
while (loc != end) {
|
for (loc = start; loc != end; loc++) {
|
||||||
if (!valid_char(*loc) && *loc != '%') {
|
if (!valid_char(*loc) && *loc != '%') {
|
||||||
if (why) {
|
if (why) {
|
||||||
*why = NAME_ERR_INVALCHAR;
|
*why = NAME_ERR_INVALCHAR;
|
||||||
@ -201,43 +185,64 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
|||||||
}
|
}
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
loc++;
|
}
|
||||||
|
|
||||||
|
/* Snapshot or bookmark delimiter found */
|
||||||
|
if (*end == '@' || *end == '#') {
|
||||||
|
/* Multiple delimiters are not allowed */
|
||||||
|
if (found_delim != 0) {
|
||||||
|
if (why)
|
||||||
|
*why = NAME_ERR_MULTIPLE_DELIMITERS;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
found_delim = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero-length components are not allowed */
|
||||||
|
if (start == end) {
|
||||||
|
if (why)
|
||||||
|
*why = NAME_ERR_EMPTY_COMPONENT;
|
||||||
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we've reached the end of the string, we're OK */
|
/* If we've reached the end of the string, we're OK */
|
||||||
if (*end == '\0')
|
if (*end == '\0')
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
if (*end == '@') {
|
|
||||||
/*
|
|
||||||
* If we've found an @ symbol, indicate that we're in
|
|
||||||
* the snapshot component, and report a second '@'
|
|
||||||
* character as an error.
|
|
||||||
*/
|
|
||||||
if (found_snapshot) {
|
|
||||||
if (why)
|
|
||||||
*why = NAME_ERR_MULTIPLE_AT;
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
found_snapshot = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is a '/' in a snapshot name
|
* If there is a '/' in a snapshot or bookmark name
|
||||||
* then report an error
|
* then report an error
|
||||||
*/
|
*/
|
||||||
if (*end == '/' && found_snapshot) {
|
if (*end == '/' && found_delim != 0) {
|
||||||
if (why)
|
if (why)
|
||||||
*why = NAME_ERR_TRAILING_SLASH;
|
*why = NAME_ERR_TRAILING_SLASH;
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update to the next component */
|
/* Update to the next component */
|
||||||
loc = end + 1;
|
start = end + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dataset is any entity, except bookmark
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||||
|
{
|
||||||
|
int ret = entity_namecheck(path, why, what);
|
||||||
|
|
||||||
|
if (ret == 0 && strchr(path, '#') != NULL) {
|
||||||
|
if (why != NULL) {
|
||||||
|
*why = NAME_ERR_INVALCHAR;
|
||||||
|
*what = '#';
|
||||||
|
}
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* mountpoint names must be of the following form:
|
* mountpoint names must be of the following form:
|
||||||
|
@ -147,6 +147,7 @@ export TESTVOL2=testvol2$$
|
|||||||
export TESTFILE0=testfile0.$$
|
export TESTFILE0=testfile0.$$
|
||||||
export TESTFILE1=testfile1.$$
|
export TESTFILE1=testfile1.$$
|
||||||
export TESTFILE2=testfile2.$$
|
export TESTFILE2=testfile2.$$
|
||||||
|
export TESTBKMARK=testbkmark$$
|
||||||
|
|
||||||
export LONGPNAME="poolname50charslong_012345678901234567890123456789"
|
export LONGPNAME="poolname50charslong_012345678901234567890123456789"
|
||||||
export LONGFSNAME="fsysname50charslong_012345678901234567890123456789"
|
export LONGFSNAME="fsysname50charslong_012345678901234567890123456789"
|
||||||
|
@ -291,6 +291,35 @@ function create_clone # snapshot clone
|
|||||||
log_must $ZFS clone $snap $clone
|
log_must $ZFS clone $snap $clone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Create a bookmark of the given snapshot. Defaultly create a bookmark on
|
||||||
|
# filesystem.
|
||||||
|
#
|
||||||
|
# $1 Existing filesystem or volume name. Default, $TESTFS
|
||||||
|
# $2 Existing snapshot name. Default, $TESTSNAP
|
||||||
|
# $3 bookmark name. Default, $TESTBKMARK
|
||||||
|
#
|
||||||
|
function create_bookmark
|
||||||
|
{
|
||||||
|
typeset fs_vol=${1:-$TESTFS}
|
||||||
|
typeset snap=${2:-$TESTSNAP}
|
||||||
|
typeset bkmark=${3:-$TESTBKMARK}
|
||||||
|
|
||||||
|
[[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined."
|
||||||
|
[[ -z $snap ]] && log_fail "Snapshot's name is undefined."
|
||||||
|
[[ -z $bkmark ]] && log_fail "Bookmark's name is undefined."
|
||||||
|
|
||||||
|
if bkmarkexists $fs_vol#$bkmark; then
|
||||||
|
log_fail "$fs_vol#$bkmark already exists."
|
||||||
|
fi
|
||||||
|
datasetexists $fs_vol || \
|
||||||
|
log_fail "$fs_vol must exist."
|
||||||
|
snapexists $fs_vol@$snap || \
|
||||||
|
log_fail "$fs_vol@$snap must exist."
|
||||||
|
|
||||||
|
log_must $ZFS bookmark $fs_vol@$snap $fs_vol#$bkmark
|
||||||
|
}
|
||||||
|
|
||||||
function default_mirror_setup
|
function default_mirror_setup
|
||||||
{
|
{
|
||||||
default_mirror_setup_noexit $1 $2 $3
|
default_mirror_setup_noexit $1 $2 $3
|
||||||
@ -575,6 +604,23 @@ function destroy_clone
|
|||||||
log_must $RM -rf $mtpt
|
log_must $RM -rf $mtpt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Common function used to cleanup bookmark of file system or volume. Default
|
||||||
|
# to delete the file system's bookmark.
|
||||||
|
#
|
||||||
|
# $1 bookmark name
|
||||||
|
#
|
||||||
|
function destroy_bookmark
|
||||||
|
{
|
||||||
|
typeset bkmark=${1:-$TESTPOOL/$TESTFS#$TESTBKMARK}
|
||||||
|
|
||||||
|
if ! bkmarkexists $bkmark; then
|
||||||
|
log_fail "'$bkmarkp' does not existed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_must $ZFS destroy $bkmark
|
||||||
|
}
|
||||||
|
|
||||||
# Return 0 if a snapshot exists; $? otherwise
|
# Return 0 if a snapshot exists; $? otherwise
|
||||||
#
|
#
|
||||||
# $1 - snapshot name
|
# $1 - snapshot name
|
||||||
@ -585,6 +631,17 @@ function snapexists
|
|||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return 0 if a bookmark exists; $? otherwise
|
||||||
|
#
|
||||||
|
# $1 - bookmark name
|
||||||
|
#
|
||||||
|
function bkmarkexists
|
||||||
|
{
|
||||||
|
$ZFS list -H -t bookmark "$1" > /dev/null 2>&1
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Set a property to a certain value on a dataset.
|
# Set a property to a certain value on a dataset.
|
||||||
# Sets a property of the dataset to the value as passed in.
|
# Sets a property of the dataset to the value as passed in.
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
# correct property value.
|
# correct property value.
|
||||||
#
|
#
|
||||||
# STRATEGY:
|
# STRATEGY:
|
||||||
# 1. Create pool, filesystem, volume and snapshot.
|
# 1. Create pool, filesystem, volume, snapshot, and bookmark.
|
||||||
# 2. Setting valid parameter, 'zfs get' should succeed.
|
# 2. Setting valid parameter, 'zfs get' should succeed.
|
||||||
# 3. Compare the output property name with the original input property.
|
# 3. Compare the output property name with the original input property.
|
||||||
#
|
#
|
||||||
@ -65,6 +65,9 @@ typeset all_props=("${zfs_props[@]}" "${userquota_props[@]}")
|
|||||||
typeset dataset=($TESTPOOL/$TESTCTR $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
|
typeset dataset=($TESTPOOL/$TESTCTR $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
|
||||||
$TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP)
|
$TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP)
|
||||||
|
|
||||||
|
typeset bookmark_props=(creation)
|
||||||
|
typeset bookmark=($TESTPOOL/$TESTFS#$TESTBKMARK $TESTPOOL/$TESTVOL#$TESTBKMARK)
|
||||||
|
|
||||||
#
|
#
|
||||||
# According to dataset and option, checking if 'zfs get' return correct
|
# According to dataset and option, checking if 'zfs get' return correct
|
||||||
# property information.
|
# property information.
|
||||||
@ -111,6 +114,10 @@ log_onexit cleanup
|
|||||||
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
|
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
|
||||||
create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
|
create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
|
||||||
|
|
||||||
|
# Create filesystem and volume's bookmark
|
||||||
|
create_bookmark $TESTPOOL/$TESTFS $TESTSNAP $TESTBKMARK
|
||||||
|
create_bookmark $TESTPOOL/$TESTVOL $TESTSNAP $TESTBKMARK
|
||||||
|
|
||||||
typeset -i i=0
|
typeset -i i=0
|
||||||
while ((i < ${#dataset[@]})); do
|
while ((i < ${#dataset[@]})); do
|
||||||
for opt in "${options[@]}"; do
|
for opt in "${options[@]}"; do
|
||||||
@ -127,5 +134,21 @@ while ((i < ${#dataset[@]})); do
|
|||||||
((i += 1))
|
((i += 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while ((i < ${#bookmark[@]})); do
|
||||||
|
for opt in "${options[@]}"; do
|
||||||
|
for prop in ${bookmark_props[@]}; do
|
||||||
|
eval "$ZFS get $opt $prop ${bookmark[i]} > \
|
||||||
|
$TESTDIR/$TESTFILE0"
|
||||||
|
ret=$?
|
||||||
|
if [[ $ret != 0 ]]; then
|
||||||
|
log_fail "$ZFS get returned: $ret"
|
||||||
|
fi
|
||||||
|
check_return_value ${bookmark[i]} "$prop" "$opt"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
((i += 1))
|
||||||
|
done
|
||||||
|
|
||||||
log_pass "Setting the valid options to dataset, it should succeed and return " \
|
log_pass "Setting the valid options to dataset, it should succeed and return " \
|
||||||
"valid value. 'zfs get' pass."
|
"valid value. 'zfs get' pass."
|
||||||
|
@ -67,6 +67,9 @@ val_props_str="$val_props_str -a -d"
|
|||||||
inval_opts_str=$(gen_option_str "${inval_opts[*]}" "-" "" $opt_numb)
|
inval_opts_str=$(gen_option_str "${inval_opts[*]}" "-" "" $opt_numb)
|
||||||
inval_props_str=$(gen_option_str "${inval_props[*]}" "" "," $prop_numb)
|
inval_props_str=$(gen_option_str "${inval_props[*]}" "" "," $prop_numb)
|
||||||
|
|
||||||
|
typeset val_bookmark_props=(creation)
|
||||||
|
typeset bookmark=($TESTPOOL/$TESTFS#$TESTBKMARK $TESTPOOL/$TESTVOL#$TESTBKMARK)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Test different options and properties combination.
|
# Test different options and properties combination.
|
||||||
#
|
#
|
||||||
@ -92,6 +95,31 @@ function test_options
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test different options and properties combination for bookmarks.
|
||||||
|
#
|
||||||
|
# $1 options
|
||||||
|
# $2 properties
|
||||||
|
#
|
||||||
|
function test_options_bookmarks
|
||||||
|
{
|
||||||
|
typeset opts=$1
|
||||||
|
typeset props=$2
|
||||||
|
|
||||||
|
for dst in ${bookmark[@]}; do
|
||||||
|
for opt in $opts; do
|
||||||
|
for prop in $props; do
|
||||||
|
$ZFS get $opt -- $prop $dst > /dev/null 2>&1
|
||||||
|
ret=$?
|
||||||
|
if [[ $ret == 0 ]]; then
|
||||||
|
log_fail "$ZFS get $opt -- $prop " \
|
||||||
|
"$dst unexpectedly succeeded."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
log_assert "Setting the invalid option and properties, 'zfs get' should be \
|
log_assert "Setting the invalid option and properties, 'zfs get' should be \
|
||||||
failed."
|
failed."
|
||||||
log_onexit cleanup
|
log_onexit cleanup
|
||||||
@ -100,13 +128,20 @@ log_onexit cleanup
|
|||||||
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
|
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
|
||||||
create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
|
create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
|
||||||
|
|
||||||
|
# Create filesystem and volume's bookmark
|
||||||
|
create_bookmark $TESTPOOL/$TESTFS $TESTSNAP $TESTBKMARK
|
||||||
|
create_bookmark $TESTPOOL/$TESTVOL $TESTSNAP $TESTBKMARK
|
||||||
|
|
||||||
log_note "Valid options + invalid properties, 'zfs get' should fail."
|
log_note "Valid options + invalid properties, 'zfs get' should fail."
|
||||||
test_options "$val_opts_str" "$inval_props_str"
|
test_options "$val_opts_str" "$inval_props_str"
|
||||||
|
test_options_bookmark "$val_opts_str" "$inval_props_str"
|
||||||
|
|
||||||
log_note "Invalid options + valid properties, 'zfs get' should fail."
|
log_note "Invalid options + valid properties, 'zfs get' should fail."
|
||||||
test_options "$inval_opts_str" "$val_props_str"
|
test_options "$inval_opts_str" "$val_props_str"
|
||||||
|
test_options_bookmark "$inval_opts_str" "$val_bookmark_props"
|
||||||
|
|
||||||
log_note "Invalid options + invalid properties, 'zfs get' should fail."
|
log_note "Invalid options + invalid properties, 'zfs get' should fail."
|
||||||
test_options "$inval_opts_str" "$inval_props_str"
|
test_options "$inval_opts_str" "$inval_props_str"
|
||||||
|
test_options_bookmarks "$inval_opts_str" "$inval_props_str"
|
||||||
|
|
||||||
log_pass "Setting the invalid options to dataset, 'zfs get' pass."
|
log_pass "Setting the invalid options to dataset, 'zfs get' pass."
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
# Verify "-d <n>" can work with other options
|
# Verify "-d <n>" can work with other options
|
||||||
#
|
#
|
||||||
# STRATEGY:
|
# STRATEGY:
|
||||||
# 1. Create pool, filesystem, dataset, volume and snapshot.
|
# 1. Create pool, filesystem, dataset, volume, snapshot, and bookmark.
|
||||||
# 2. Getting an -d option, other options and properties random combination.
|
# 2. Getting an -d option, other options and properties random combination.
|
||||||
# 3. Using the combination as the parameters of 'zfs get' to check the
|
# 3. Using the combination as the parameters of 'zfs get' to check the
|
||||||
# command line return value.
|
# command line return value.
|
||||||
@ -61,6 +61,9 @@ fi
|
|||||||
set -A dataset $TESTPOOL/$TESTCTR $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
|
set -A dataset $TESTPOOL/$TESTCTR $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
|
||||||
$TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP
|
$TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP
|
||||||
|
|
||||||
|
set -A bookmark_props creation
|
||||||
|
set -A bookmark $TESTPOOL/$TESTFS#$TESTBKMARK $TESTPOOL/$TESTVOL#$TESTBKMARK
|
||||||
|
|
||||||
log_assert "Verify '-d <n>' can work with other options"
|
log_assert "Verify '-d <n>' can work with other options"
|
||||||
log_onexit cleanup
|
log_onexit cleanup
|
||||||
|
|
||||||
@ -68,6 +71,10 @@ log_onexit cleanup
|
|||||||
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
|
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
|
||||||
create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
|
create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
|
||||||
|
|
||||||
|
# Create filesystem and volume's bookmark
|
||||||
|
create_bookmark $TESTPOOL/$TESTFS $TESTSNAP $TESTBKMARK
|
||||||
|
create_bookmark $TESTPOOL/$TESTVOL $TESTSNAP $TESTBKMARK
|
||||||
|
|
||||||
typeset -i opt_numb=16
|
typeset -i opt_numb=16
|
||||||
typeset -i prop_numb=16
|
typeset -i prop_numb=16
|
||||||
typeset -i i=0
|
typeset -i i=0
|
||||||
@ -87,5 +94,15 @@ for dst in ${dataset[@]}; do
|
|||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for dst in ${bookmark[@]}; do
|
||||||
|
(( i=0 ))
|
||||||
|
while (( i < opt_numb )); do
|
||||||
|
(( item = $RANDOM % ${#options[@]} ))
|
||||||
|
(( depth_item = $RANDOM % ${#depth_options[@]} ))
|
||||||
|
log_must eval "$ZFS get -${depth_options[depth_item]} ${options[item]} $bookmark_props $dst > /dev/null 2>&1"
|
||||||
|
(( i += 1 ))
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
log_pass "Verify '-d <n>' can work with other options"
|
log_pass "Verify '-d <n>' can work with other options"
|
||||||
|
|
||||||
|
@ -83,8 +83,8 @@ function gen_option_str # $elements $prefix $separator $counter
|
|||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Cleanup the volume snapshot and filesystem snapshot were created for
|
# Cleanup the volume snapshot, filesystem snapshot, volume bookmark, and
|
||||||
# this test case.
|
# filesystem bookmark that were created for this test case.
|
||||||
#
|
#
|
||||||
function cleanup
|
function cleanup
|
||||||
{
|
{
|
||||||
@ -93,5 +93,10 @@ function cleanup
|
|||||||
datasetexists $TESTPOOL/$TESTFS@$TESTSNAP && \
|
datasetexists $TESTPOOL/$TESTFS@$TESTSNAP && \
|
||||||
destroy_snapshot $TESTPOOL/$TESTFS@$TESTSNAP
|
destroy_snapshot $TESTPOOL/$TESTFS@$TESTSNAP
|
||||||
|
|
||||||
|
bkmarkexists $TESTPOOL/$TESTVOL#$TESTBKMARK && \
|
||||||
|
destroy_bookmark $TESTPOOL/$TESTVOL#$TESTBKMARK
|
||||||
|
bkmarkexists $TESTPOOL/$TESTFS#$TESTBKMARK && \
|
||||||
|
destroy_bookmark $TESTPOOL/$TESTFS#$TESTBKMARK
|
||||||
|
|
||||||
[[ -e $TESTFILE0 ]] && log_must $RM $TESTFILE0
|
[[ -e $TESTFILE0 ]] && log_must $RM $TESTFILE0
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user