MFV r260154 + 260182:
4369 implement zfs bookmarks 4368 zfs send filesystems from readonly pools Illumos/illumos-gate@78f1710053 MFC after: 2 weeks
This commit is contained in:
commit
5137277761
@ -18,7 +18,7 @@
|
||||
.\" information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
.\"
|
||||
.\" Copyright (c) 2010, Sun Microsystems, Inc. All Rights Reserved.
|
||||
.\" Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
.\" Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
.\" Copyright (c) 2011, Pawel Jakub Dawidek <pjd@FreeBSD.org>
|
||||
.\" Copyright (c) 2012, Glen Barber <gjb@FreeBSD.org>
|
||||
.\" Copyright (c) 2012, Bryan Drewery <bdrewery@FreeBSD.org>
|
||||
@ -26,10 +26,11 @@
|
||||
.\" Copyright (c) 2013 Nexenta Systems, Inc. All Rights Reserved.
|
||||
.\" Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
.\" Copyright (c) 2013, Steven Hartland <smh@FreeBSD.org>
|
||||
.\" Copyright (c) 2014, Xin LI <delphij@FreeBSD.org>
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd December 24, 2013
|
||||
.Dd January 2, 2014
|
||||
.Dt ZFS 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -57,11 +58,16 @@
|
||||
.Cm destroy
|
||||
.Op Fl dnpRrv
|
||||
.Sm off
|
||||
.Ar snapshot
|
||||
.Op % Ns Ar snapname
|
||||
.Ar filesystem Ns | Ns volume
|
||||
.Ns @snap
|
||||
.Op % Ns Ar snap
|
||||
.Op , Ns Ar snap Op % Ns Ar snap
|
||||
.Op , Ns ...
|
||||
.Sm on
|
||||
.Nm
|
||||
.Cm destroy
|
||||
.Ar filesystem Ns | Ns Ar volume Ns # Ns Ar bookmark
|
||||
.Nm
|
||||
.Cm snapshot Ns | Ns Cm snap
|
||||
.Op Fl r
|
||||
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
||||
@ -168,11 +174,19 @@
|
||||
.Cm unshare
|
||||
.Fl a | Ar filesystem Ns | Ns Ar mountpoint
|
||||
.Nm
|
||||
.Cm bookmark
|
||||
.Ar snapshot
|
||||
.Ar bookmark
|
||||
.Nm
|
||||
.Cm send
|
||||
.Op Fl DnPpRv
|
||||
.Op Fl i Ar snapshot | Fl I Ar snapshot
|
||||
.Ar snapshot
|
||||
.Nm
|
||||
.Cm send
|
||||
.Op Fl i Ar snapshot Ns | Ns bookmark
|
||||
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
|
||||
.Nm
|
||||
.Cm receive Ns | Ns Cm recv
|
||||
.Op Fl vnFu
|
||||
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
|
||||
@ -1654,6 +1668,13 @@ options, as they can destroy large portions of a pool and cause unexpected
|
||||
behavior for mounted file systems in use.
|
||||
.It Xo
|
||||
.Nm
|
||||
.Cm destroy
|
||||
.Ar filesystem Ns | Ns Ar volume Ns # Ns Ar bookmark
|
||||
.Xc
|
||||
.Pp
|
||||
The given bookmark is destroyed.
|
||||
.It Xo
|
||||
.Nm
|
||||
.Cm snapshot Ns | Ns Cm snap
|
||||
.Op Fl r
|
||||
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
||||
@ -1686,14 +1707,24 @@ Roll back the given dataset to a previous snapshot. When a dataset is rolled
|
||||
back, all data that has changed since the snapshot is discarded, and the
|
||||
dataset reverts to the state at the time of the snapshot. By default, the
|
||||
command refuses to roll back to a snapshot other than the most recent one. In
|
||||
order to do so, all intermediate snapshots must be destroyed by specifying the
|
||||
order to do so, all intermediate snapshots and bookmarks must be destroyed
|
||||
by specifying the
|
||||
.Fl r
|
||||
option.
|
||||
.Pp
|
||||
The
|
||||
.Fl rR
|
||||
options do not recursively destroy the child snapshots of a
|
||||
recursive snapshot.
|
||||
Only direct snapshots of the specified filesystem
|
||||
are destroyed by either of these options.
|
||||
To completely roll back a
|
||||
recursive snapshot, you must rollback the individual child snapshots.
|
||||
.Bl -tag -width indent
|
||||
.It Fl r
|
||||
Recursively destroy any snapshots more recent than the one specified.
|
||||
Destroy any snapshots and bookmarks more recent than the one specified.
|
||||
.It Fl R
|
||||
Recursively destroy any more recent snapshots, as well as any clones of those
|
||||
Destroy any more recent snapshots and bookmarks, as well as any clones of those
|
||||
snapshots.
|
||||
.It Fl f
|
||||
Used with the
|
||||
@ -1868,7 +1899,7 @@ syntax.
|
||||
A comma-separated list of types to display, where
|
||||
.Ar type
|
||||
is one of
|
||||
.Sy filesystem , snapshot , snap, volume , No or Sy all .
|
||||
.Sy filesystem , snapshot , snap , volume , bookmark , No or Sy all .
|
||||
For example, specifying
|
||||
.Fl t Cm snapshot
|
||||
displays only snapshots.
|
||||
@ -1965,7 +1996,7 @@ sections.
|
||||
The special value
|
||||
.Cm all
|
||||
can be used to display all properties that apply to the given dataset's type
|
||||
(filesystem, volume, or snapshot).
|
||||
(filesystem, volume, snapshot, or bookmark).
|
||||
.Bl -tag -width indent
|
||||
.It Fl r
|
||||
Recursively display properties for any children.
|
||||
@ -2283,6 +2314,26 @@ file system shared on the system.
|
||||
.El
|
||||
.It Xo
|
||||
.Nm
|
||||
.Cm bookmark
|
||||
.Ar snapshot
|
||||
.Ar bookmark
|
||||
.Xc
|
||||
.Pp
|
||||
Creates a bookmark of the given snapshot.
|
||||
Bookmarks mark the point in time
|
||||
when the snapshot was created, and can be used as the incremental source for
|
||||
a
|
||||
.Qq Nm Cm send
|
||||
command.
|
||||
.Pp
|
||||
This feature must be enabled to be used.
|
||||
See
|
||||
.Xr zpool-features 7
|
||||
for details on ZFS feature flags and the
|
||||
.Sy bookmark
|
||||
feature.
|
||||
.It Xo
|
||||
.Nm
|
||||
.Cm send
|
||||
.Op Fl DnPpRv
|
||||
.Op Fl i Ar snapshot | Fl I Ar snapshot
|
||||
@ -2301,17 +2352,15 @@ a file or to a different system (for example, using
|
||||
By default, a full stream is generated.
|
||||
.Bl -tag -width indent
|
||||
.It Fl i Ar snapshot
|
||||
Generate an incremental stream from the
|
||||
.Fl i Ar snapshot
|
||||
to the last
|
||||
.Ar snapshot .
|
||||
The incremental source (the
|
||||
.Fl i Ar snapshot )
|
||||
can be specified as the last component of the snapshot name (for example, the
|
||||
part after the
|
||||
.Sy @ ) ,
|
||||
and it is assumed to be from the same file system as the last
|
||||
.Ar snapshot .
|
||||
Generate an incremental stream from the first
|
||||
.Ar snapshot Pq the incremental source
|
||||
to the second
|
||||
.Ar snapshot Pq the incremental target .
|
||||
The incremental source can be specified as the last component of the
|
||||
snapshot name
|
||||
.Pq the Em @ No character and following
|
||||
and
|
||||
it is assumed to be from the same file system as the incremental target.
|
||||
.Pp
|
||||
If the destination is a clone, the source may be the origin snapshot, which
|
||||
must be fully specified (for example,
|
||||
@ -2319,15 +2368,16 @@ must be fully specified (for example,
|
||||
not just
|
||||
.Cm @origin ) .
|
||||
.It Fl I Ar snapshot
|
||||
Generate a stream package that sends all intermediary snapshots from the
|
||||
.Fl I Ar snapshot
|
||||
to the last
|
||||
Generate a stream package that sends all intermediary snapshots from the first
|
||||
.Ar snapshot
|
||||
to the second
|
||||
.Ar snapshot .
|
||||
For example,
|
||||
.Ic -I @a fs@d
|
||||
is similar to
|
||||
.Ic -i @a fs@b; -i @b fs@c; -i @c fs@d .
|
||||
The incremental source snapshot may be specified as with the
|
||||
The incremental
|
||||
source may be specified as with the
|
||||
.Fl i
|
||||
option.
|
||||
.It Fl R
|
||||
@ -2380,6 +2430,35 @@ on future versions of
|
||||
.Tn ZFS .
|
||||
.It Xo
|
||||
.Nm
|
||||
.Cm send
|
||||
.Op Fl i Ar snapshot Ns | Ns Ar bookmark
|
||||
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
|
||||
.Xc
|
||||
.Pp
|
||||
Generate a send stream, which may be of a filesystem, and may be
|
||||
incremental from a bookmark.
|
||||
If the destination is a filesystem or volume,
|
||||
the pool must be read-only, or the filesystem must not be mounted.
|
||||
When the
|
||||
stream generated from a filesystem or volume is received, the default snapshot
|
||||
name will be
|
||||
.Pq --head-- .
|
||||
.Bl -tag -width indent
|
||||
.It Fl i Ar snapshot Ns | Ns bookmark
|
||||
Generate an incremental send stream.
|
||||
The incremental source must be an earlier
|
||||
snapshot in the destination's history.
|
||||
It will commonly be an earlier
|
||||
snapshot in the destination's filesystem, in which case it can be
|
||||
specified as the last component of the name
|
||||
.Pq the Em # No or Em @ No character and following .
|
||||
.Pp
|
||||
If the incremental target is a clone, the incremental source can
|
||||
be the origin snapshot, or an earlier snapshot in the origin's filesystem,
|
||||
or the origin's origin, etc.
|
||||
.El
|
||||
.It Xo
|
||||
.Nm
|
||||
.Cm receive Ns | Ns Cm recv
|
||||
.Op Fl vnFu
|
||||
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
|
||||
|
@ -24,6 +24,7 @@
|
||||
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
||||
* All rights reserved.
|
||||
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <libintl.h>
|
||||
@ -72,7 +73,7 @@ uu_avl_pool_t *avl_pool;
|
||||
* Include snaps if they were requested or if this a zfs list where types
|
||||
* were not specified and the "listsnapshots" property is set on this pool.
|
||||
*/
|
||||
static int
|
||||
static boolean_t
|
||||
zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb)
|
||||
{
|
||||
zpool_handle_t *zph;
|
||||
@ -92,8 +93,9 @@ static int
|
||||
zfs_callback(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
callback_data_t *cb = data;
|
||||
int dontclose = 0;
|
||||
int include_snaps = zfs_include_snapshots(zhp, cb);
|
||||
boolean_t dontclose = B_FALSE;
|
||||
boolean_t include_snaps = zfs_include_snapshots(zhp, cb);
|
||||
boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK);
|
||||
|
||||
if ((zfs_get_type(zhp) & cb->cb_types) ||
|
||||
((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {
|
||||
@ -119,7 +121,7 @@ zfs_callback(zfs_handle_t *zhp, void *data)
|
||||
}
|
||||
}
|
||||
uu_avl_insert(cb->cb_avl, node, idx);
|
||||
dontclose = 1;
|
||||
dontclose = B_TRUE;
|
||||
} else {
|
||||
free(node);
|
||||
}
|
||||
@ -134,11 +136,14 @@ zfs_callback(zfs_handle_t *zhp, void *data)
|
||||
cb->cb_depth++;
|
||||
if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
|
||||
(void) zfs_iter_filesystems(zhp, zfs_callback, data);
|
||||
if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps) {
|
||||
if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_snaps)
|
||||
(void) zfs_iter_snapshots(zhp,
|
||||
(cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback,
|
||||
data);
|
||||
}
|
||||
if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks)
|
||||
(void) zfs_iter_bookmarks(zhp, zfs_callback, data);
|
||||
cb->cb_depth--;
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,7 @@ static int zfs_do_release(int argc, char **argv);
|
||||
static int zfs_do_diff(int argc, char **argv);
|
||||
static int zfs_do_jail(int argc, char **argv);
|
||||
static int zfs_do_unjail(int argc, char **argv);
|
||||
static int zfs_do_bookmark(int argc, char **argv);
|
||||
|
||||
/*
|
||||
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
|
||||
@ -155,6 +156,7 @@ typedef enum {
|
||||
HELP_HOLDS,
|
||||
HELP_RELEASE,
|
||||
HELP_DIFF,
|
||||
HELP_BOOKMARK,
|
||||
} zfs_help_t;
|
||||
|
||||
typedef struct zfs_command {
|
||||
@ -181,6 +183,7 @@ static zfs_command_t command_table[] = {
|
||||
{ "clone", zfs_do_clone, HELP_CLONE },
|
||||
{ "promote", zfs_do_promote, HELP_PROMOTE },
|
||||
{ "rename", zfs_do_rename, HELP_RENAME },
|
||||
{ "bookmark", zfs_do_bookmark, HELP_BOOKMARK },
|
||||
{ NULL },
|
||||
{ "list", zfs_do_list, HELP_LIST },
|
||||
{ NULL },
|
||||
@ -231,11 +234,12 @@ get_usage(zfs_help_t idx)
|
||||
case HELP_DESTROY:
|
||||
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
|
||||
"\tdestroy [-dnpRrv] "
|
||||
"<snapshot>[%<snapname>][,...]\n"));
|
||||
"<filesystem|volume>@<snap>[%<snap>][,...]\n"
|
||||
"\tdestroy <filesystem|volume>#<bookmark>\n"));
|
||||
case HELP_GET:
|
||||
return (gettext("\tget [-rHp] [-d max] "
|
||||
"[-o \"all\" | field[,...]] [-t type[,...]] "
|
||||
"[-s source[,...]]\n"
|
||||
"[-o \"all\" | field[,...]]\n"
|
||||
"\t [-t type[,...]] [-s source[,...]]\n"
|
||||
"\t <\"all\" | property[,...]> "
|
||||
"[filesystem|volume|snapshot] ...\n"));
|
||||
case HELP_INHERIT:
|
||||
@ -264,15 +268,16 @@ get_usage(zfs_help_t idx)
|
||||
case HELP_RENAME:
|
||||
return (gettext("\trename [-f] <filesystem|volume|snapshot> "
|
||||
"<filesystem|volume|snapshot>\n"
|
||||
"\trename [-f] -p <filesystem|volume> "
|
||||
"<filesystem|volume>\n"
|
||||
"\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
|
||||
"\trename -r <snapshot> <snapshot>\n"
|
||||
"\trename -u [-p] <filesystem> <filesystem>"));
|
||||
case HELP_ROLLBACK:
|
||||
return (gettext("\trollback [-rRf] <snapshot>\n"));
|
||||
case HELP_SEND:
|
||||
return (gettext("\tsend [-DnPpRv] "
|
||||
"[-i snapshot | -I snapshot] <snapshot>\n"));
|
||||
return (gettext("\tsend [-DnPpRv] [-[iI] snapshot] "
|
||||
"<snapshot>\n"
|
||||
"\tsend [-i snapshot|bookmark] "
|
||||
"<filesystem|volume|snapshot>\n"));
|
||||
case HELP_SET:
|
||||
return (gettext("\tset <property=value> "
|
||||
"<filesystem|volume|snapshot> ...\n"));
|
||||
@ -280,7 +285,7 @@ get_usage(zfs_help_t idx)
|
||||
return (gettext("\tshare <-a | filesystem>\n"));
|
||||
case HELP_SNAPSHOT:
|
||||
return (gettext("\tsnapshot|snap [-r] [-o property=value] ... "
|
||||
"<filesystem@snapname|volume@snapname> ...\n"));
|
||||
"<filesystem|volume>@<snap> ...\n"));
|
||||
case HELP_UNMOUNT:
|
||||
return (gettext("\tunmount|umount [-f] "
|
||||
"<-a | filesystem|mountpoint>\n"));
|
||||
@ -309,11 +314,13 @@ get_usage(zfs_help_t idx)
|
||||
"<filesystem|volume>\n"));
|
||||
case HELP_USERSPACE:
|
||||
return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
|
||||
"[-s field]...\n\t [-S field]... [-t type[,...]] "
|
||||
"[-s field] ...\n"
|
||||
"\t [-S field] ... [-t type[,...]] "
|
||||
"<filesystem|snapshot>\n"));
|
||||
case HELP_GROUPSPACE:
|
||||
return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
|
||||
"[-s field]...\n\t [-S field]... [-t type[,...]] "
|
||||
"[-s field] ...\n"
|
||||
"\t [-S field] ... [-t type[,...]] "
|
||||
"<filesystem|snapshot>\n"));
|
||||
case HELP_HOLD:
|
||||
return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
|
||||
@ -324,6 +331,8 @@ get_usage(zfs_help_t idx)
|
||||
case HELP_DIFF:
|
||||
return (gettext("\tdiff [-FHt] <snapshot> "
|
||||
"[snapshot|filesystem]\n"));
|
||||
case HELP_BOOKMARK:
|
||||
return (gettext("\tbookmark <snapshot> <bookmark>\n"));
|
||||
}
|
||||
|
||||
abort();
|
||||
@ -926,6 +935,7 @@ typedef struct destroy_cbdata {
|
||||
char *cb_prevsnap;
|
||||
int64_t cb_snapused;
|
||||
char *cb_snapspec;
|
||||
char *cb_bookmark;
|
||||
} destroy_cbdata_t;
|
||||
|
||||
/*
|
||||
@ -1195,7 +1205,7 @@ zfs_do_destroy(int argc, char **argv)
|
||||
int err = 0;
|
||||
int c;
|
||||
zfs_handle_t *zhp = NULL;
|
||||
char *at;
|
||||
char *at, *pound;
|
||||
zfs_type_t type = ZFS_TYPE_DATASET;
|
||||
|
||||
/* check options */
|
||||
@ -1247,6 +1257,7 @@ zfs_do_destroy(int argc, char **argv)
|
||||
}
|
||||
|
||||
at = strchr(argv[0], '@');
|
||||
pound = strchr(argv[0], '#');
|
||||
if (at != NULL) {
|
||||
|
||||
/* Build the list of snaps to destroy in cb_nvl. */
|
||||
@ -1308,6 +1319,46 @@ zfs_do_destroy(int argc, char **argv)
|
||||
|
||||
if (err != 0)
|
||||
rv = 1;
|
||||
} else if (pound != NULL) {
|
||||
int err;
|
||||
nvlist_t *nvl;
|
||||
|
||||
if (cb.cb_dryrun) {
|
||||
(void) fprintf(stderr,
|
||||
"dryrun is not supported with bookmark\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (cb.cb_defer_destroy) {
|
||||
(void) fprintf(stderr,
|
||||
"defer destroy is not supported with bookmark\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (cb.cb_recurse) {
|
||||
(void) fprintf(stderr,
|
||||
"recursive is not supported with bookmark\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (!zfs_bookmark_exists(argv[0])) {
|
||||
(void) fprintf(stderr, gettext("bookmark '%s' "
|
||||
"does not exist.\n"), argv[0]);
|
||||
return (1);
|
||||
}
|
||||
|
||||
nvl = fnvlist_alloc();
|
||||
fnvlist_add_boolean(nvl, argv[0]);
|
||||
|
||||
err = lzc_destroy_bookmarks(nvl, NULL);
|
||||
if (err != 0) {
|
||||
(void) zfs_standard_error(g_zfs, err,
|
||||
"cannot destroy bookmark");
|
||||
}
|
||||
|
||||
nvlist_free(cb.cb_nvl);
|
||||
|
||||
return (err);
|
||||
} else {
|
||||
/* Open the given dataset */
|
||||
if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
|
||||
@ -1670,7 +1721,8 @@ zfs_do_get(int argc, char **argv)
|
||||
flags &= ~ZFS_ITER_PROP_LISTSNAPS;
|
||||
while (*optarg != '\0') {
|
||||
static char *type_subopts[] = { "filesystem",
|
||||
"volume", "snapshot", "all", NULL };
|
||||
"volume", "snapshot", "bookmark",
|
||||
"all", NULL };
|
||||
|
||||
switch (getsubopt(&optarg, type_subopts,
|
||||
&value)) {
|
||||
@ -1684,7 +1736,11 @@ zfs_do_get(int argc, char **argv)
|
||||
types |= ZFS_TYPE_SNAPSHOT;
|
||||
break;
|
||||
case 3:
|
||||
types = ZFS_TYPE_DATASET;
|
||||
types |= ZFS_TYPE_BOOKMARK;
|
||||
break;
|
||||
case 4:
|
||||
types = ZFS_TYPE_DATASET |
|
||||
ZFS_TYPE_BOOKMARK;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -3029,7 +3085,8 @@ zfs_do_list(int argc, char **argv)
|
||||
flags &= ~ZFS_ITER_PROP_LISTSNAPS;
|
||||
while (*optarg != '\0') {
|
||||
static char *type_subopts[] = { "filesystem",
|
||||
"volume", "snapshot", "snap", "all", NULL };
|
||||
"volume", "snapshot", "snap", "bookmark",
|
||||
"all", NULL };
|
||||
|
||||
switch (getsubopt(&optarg, type_subopts,
|
||||
&value)) {
|
||||
@ -3044,9 +3101,12 @@ zfs_do_list(int argc, char **argv)
|
||||
types |= ZFS_TYPE_SNAPSHOT;
|
||||
break;
|
||||
case 4:
|
||||
types = ZFS_TYPE_DATASET;
|
||||
types |= ZFS_TYPE_BOOKMARK;
|
||||
break;
|
||||
case 5:
|
||||
types = ZFS_TYPE_DATASET |
|
||||
ZFS_TYPE_BOOKMARK;
|
||||
break;
|
||||
|
||||
default:
|
||||
(void) fprintf(stderr,
|
||||
gettext("invalid type '%s'\n"),
|
||||
@ -3286,9 +3346,29 @@ typedef struct rollback_cbdata {
|
||||
char *cb_target;
|
||||
int cb_error;
|
||||
boolean_t cb_recurse;
|
||||
boolean_t cb_dependent;
|
||||
} rollback_cbdata_t;
|
||||
|
||||
static int
|
||||
rollback_check_dependent(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
rollback_cbdata_t *cbp = data;
|
||||
|
||||
if (cbp->cb_first && cbp->cb_recurse) {
|
||||
(void) fprintf(stderr, gettext("cannot rollback to "
|
||||
"'%s': clones of previous snapshots exist\n"),
|
||||
cbp->cb_target);
|
||||
(void) fprintf(stderr, gettext("use '-R' to "
|
||||
"force deletion of the following clones and "
|
||||
"dependents:\n"));
|
||||
cbp->cb_first = 0;
|
||||
cbp->cb_error = 1;
|
||||
}
|
||||
|
||||
(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
|
||||
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
* Report any snapshots more recent than the one specified. Used when '-r' is
|
||||
* not specified. We reuse this same callback for the snapshot dependents - if
|
||||
@ -3305,52 +3385,30 @@ rollback_check(zfs_handle_t *zhp, void *data)
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (!cbp->cb_dependent) {
|
||||
if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 &&
|
||||
zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
|
||||
cbp->cb_create) {
|
||||
|
||||
if (cbp->cb_first && !cbp->cb_recurse) {
|
||||
(void) fprintf(stderr, gettext("cannot "
|
||||
"rollback to '%s': more recent snapshots "
|
||||
"exist\n"),
|
||||
cbp->cb_target);
|
||||
(void) fprintf(stderr, gettext("use '-r' to "
|
||||
"force deletion of the following "
|
||||
"snapshots:\n"));
|
||||
cbp->cb_first = 0;
|
||||
cbp->cb_error = 1;
|
||||
}
|
||||
|
||||
if (cbp->cb_recurse) {
|
||||
cbp->cb_dependent = B_TRUE;
|
||||
if (zfs_iter_dependents(zhp, B_TRUE,
|
||||
rollback_check, cbp) != 0) {
|
||||
zfs_close(zhp);
|
||||
return (-1);
|
||||
}
|
||||
cbp->cb_dependent = B_FALSE;
|
||||
} else {
|
||||
(void) fprintf(stderr, "%s\n",
|
||||
zfs_get_name(zhp));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cbp->cb_first && cbp->cb_recurse) {
|
||||
(void) fprintf(stderr, gettext("cannot rollback to "
|
||||
"'%s': clones of previous snapshots exist\n"),
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
|
||||
if (cbp->cb_first && !cbp->cb_recurse) {
|
||||
(void) fprintf(stderr, gettext("cannot "
|
||||
"rollback to '%s': more recent snapshots "
|
||||
"or bookmarks exist\n"),
|
||||
cbp->cb_target);
|
||||
(void) fprintf(stderr, gettext("use '-R' to "
|
||||
"force deletion of the following clones and "
|
||||
"dependents:\n"));
|
||||
(void) fprintf(stderr, gettext("use '-r' to "
|
||||
"force deletion of the following "
|
||||
"snapshots and bookmarks:\n"));
|
||||
cbp->cb_first = 0;
|
||||
cbp->cb_error = 1;
|
||||
}
|
||||
|
||||
(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
|
||||
if (cbp->cb_recurse) {
|
||||
if (zfs_iter_dependents(zhp, B_TRUE,
|
||||
rollback_check_dependent, cbp) != 0) {
|
||||
zfs_close(zhp);
|
||||
return (-1);
|
||||
}
|
||||
} else {
|
||||
(void) fprintf(stderr, "%s\n",
|
||||
zfs_get_name(zhp));
|
||||
}
|
||||
}
|
||||
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
@ -3420,7 +3478,9 @@ zfs_do_rollback(int argc, char **argv)
|
||||
cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
|
||||
cb.cb_first = B_TRUE;
|
||||
cb.cb_error = 0;
|
||||
if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0)
|
||||
if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb)) != 0)
|
||||
goto out;
|
||||
if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
|
||||
goto out;
|
||||
|
||||
if ((ret = cb.cb_error) != 0)
|
||||
@ -3715,12 +3775,45 @@ zfs_do_send(int argc, char **argv)
|
||||
return (1);
|
||||
}
|
||||
|
||||
cp = strchr(argv[0], '@');
|
||||
if (cp == NULL) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("argument must be a snapshot\n"));
|
||||
usage(B_FALSE);
|
||||
/*
|
||||
* Special case sending a filesystem, or from a bookmark.
|
||||
*/
|
||||
if (strchr(argv[0], '@') == NULL ||
|
||||
(fromname && strchr(fromname, '#') != NULL)) {
|
||||
char frombuf[ZFS_MAXNAMELEN];
|
||||
|
||||
if (flags.replicate || flags.doall || flags.props ||
|
||||
flags.dedup || flags.dryrun || flags.verbose ||
|
||||
flags.progress) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("Error: "
|
||||
"Unsupported flag with filesystem or bookmark.\n"));
|
||||
return (1);
|
||||
}
|
||||
|
||||
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
|
||||
if (zhp == NULL)
|
||||
return (1);
|
||||
|
||||
if (fromname != NULL &&
|
||||
(fromname[0] == '#' || fromname[0] == '@')) {
|
||||
/*
|
||||
* Incremental source name begins with # or @.
|
||||
* Default to same fs as target.
|
||||
*/
|
||||
(void) strncpy(frombuf, argv[0], sizeof (frombuf));
|
||||
cp = strchr(frombuf, '@');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
(void) strlcat(frombuf, fromname, sizeof (frombuf));
|
||||
fromname = frombuf;
|
||||
}
|
||||
err = zfs_send_one(zhp, fromname, STDOUT_FILENO);
|
||||
zfs_close(zhp);
|
||||
return (err != 0);
|
||||
}
|
||||
|
||||
cp = strchr(argv[0], '@');
|
||||
*cp = '\0';
|
||||
toname = cp + 1;
|
||||
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
||||
@ -3876,6 +3969,7 @@ zfs_do_receive(int argc, char **argv)
|
||||
#define ZFS_DELEG_PERM_HOLD "hold"
|
||||
#define ZFS_DELEG_PERM_RELEASE "release"
|
||||
#define ZFS_DELEG_PERM_DIFF "diff"
|
||||
#define ZFS_DELEG_PERM_BOOKMARK "bookmark"
|
||||
|
||||
#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
|
||||
|
||||
@ -3895,6 +3989,7 @@ static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
|
||||
{ ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
|
||||
{ ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
|
||||
{ ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
|
||||
{ ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
|
||||
|
||||
{ ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
|
||||
{ ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
|
||||
@ -6666,6 +6761,108 @@ zfs_do_diff(int argc, char **argv)
|
||||
return (err != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* zfs bookmark <fs@snap> <fs#bmark>
|
||||
*
|
||||
* Creates a bookmark with the given name from the given snapshot.
|
||||
*/
|
||||
static int
|
||||
zfs_do_bookmark(int argc, char **argv)
|
||||
{
|
||||
char snapname[ZFS_MAXNAMELEN];
|
||||
zfs_handle_t *zhp;
|
||||
nvlist_t *nvl;
|
||||
int ret = 0;
|
||||
int c;
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "")) != -1) {
|
||||
switch (c) {
|
||||
case '?':
|
||||
(void) fprintf(stderr,
|
||||
gettext("invalid option '%c'\n"), optopt);
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* check number of arguments */
|
||||
if (argc < 1) {
|
||||
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
|
||||
goto usage;
|
||||
}
|
||||
if (argc < 2) {
|
||||
(void) fprintf(stderr, gettext("missing bookmark argument\n"));
|
||||
goto usage;
|
||||
}
|
||||
|
||||
if (strchr(argv[1], '#') == NULL) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("invalid bookmark name '%s' -- "
|
||||
"must contain a '#'\n"), argv[1]);
|
||||
goto usage;
|
||||
}
|
||||
|
||||
if (argv[0][0] == '@') {
|
||||
/*
|
||||
* Snapshot name begins with @.
|
||||
* Default to same fs as bookmark.
|
||||
*/
|
||||
(void) strncpy(snapname, argv[1], sizeof (snapname));
|
||||
*strchr(snapname, '#') = '\0';
|
||||
(void) strlcat(snapname, argv[0], sizeof (snapname));
|
||||
} else {
|
||||
(void) strncpy(snapname, argv[0], sizeof (snapname));
|
||||
}
|
||||
zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT);
|
||||
if (zhp == NULL)
|
||||
goto usage;
|
||||
zfs_close(zhp);
|
||||
|
||||
|
||||
nvl = fnvlist_alloc();
|
||||
fnvlist_add_string(nvl, argv[1], snapname);
|
||||
ret = lzc_bookmark(nvl, NULL);
|
||||
fnvlist_free(nvl);
|
||||
|
||||
if (ret != 0) {
|
||||
const char *err_msg;
|
||||
char errbuf[1024];
|
||||
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot create bookmark '%s'"), argv[1]);
|
||||
|
||||
switch (ret) {
|
||||
case EXDEV:
|
||||
err_msg = "bookmark is in a different pool";
|
||||
break;
|
||||
case EEXIST:
|
||||
err_msg = "bookmark exists";
|
||||
break;
|
||||
case EINVAL:
|
||||
err_msg = "invalid argument";
|
||||
break;
|
||||
case ENOTSUP:
|
||||
err_msg = "bookmark feature not enabled";
|
||||
break;
|
||||
default:
|
||||
err_msg = "unknown error";
|
||||
break;
|
||||
}
|
||||
(void) fprintf(stderr, "%s: %s\n", errbuf,
|
||||
dgettext(TEXT_DOMAIN, err_msg));
|
||||
}
|
||||
|
||||
return (ret);
|
||||
|
||||
usage:
|
||||
usage(B_FALSE);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
|
@ -23,7 +23,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd December 31, 2013
|
||||
.Dd January 2, 2014
|
||||
.Dt ZPOOL-FEATURES 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -222,12 +222,16 @@ command. Please note that doing so will
|
||||
immediately activate the
|
||||
.Sy lz4_compress
|
||||
feature on the underlying
|
||||
pool (even before any data is written). Since this feature is not
|
||||
read-only compatible, this operation will render the pool unimportable
|
||||
on systems without support for the
|
||||
pool
|
||||
.Pq even before any data is written ,
|
||||
and the feature will not be
|
||||
deactivated.
|
||||
Since this feature is not read-only compatible, this
|
||||
operation will render the pool unimportable on systems without support
|
||||
for the
|
||||
.Sy lz4_compress
|
||||
feature. At the
|
||||
moment, this operation cannot be reversed. Booting off of
|
||||
feature.
|
||||
Booting off of
|
||||
.Sy lz4
|
||||
-compressed root pools is supported.
|
||||
.It Sy multi_vdev_crash_dump
|
||||
@ -286,6 +290,25 @@ and will be returned to the
|
||||
.Sy enabled
|
||||
state when all datasets that use
|
||||
this feature are destroyed.
|
||||
.It Sy bookmarks
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:bookmarks"
|
||||
.It GUID Ta com.delphix:bookmarks
|
||||
.It READ\-ONLY COMPATIBLE Ta yes
|
||||
.It DEPENDENCIES Ta extensible_dataset
|
||||
.El
|
||||
.Pp
|
||||
This feature enables use of the
|
||||
.Nm zfs
|
||||
.Cm bookmark
|
||||
subcommand.
|
||||
.Pp
|
||||
This feature is
|
||||
.Sy active
|
||||
while any bookmarks exist in the pool.
|
||||
All bookmarks in the pool can be listed by running
|
||||
.Nm zfs
|
||||
.Cm list
|
||||
.Fl t No bookmark Fl r Ar poolname .
|
||||
.It Sy enabled_txg
|
||||
.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:enabled_txg"
|
||||
.It GUID Ta com.delphix:enabled_txg
|
||||
|
@ -23,7 +23,7 @@
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||
@ -193,6 +193,7 @@ extern int zpool_log_history(libzfs_handle_t *, const char *);
|
||||
extern int libzfs_errno(libzfs_handle_t *);
|
||||
extern const char *libzfs_error_action(libzfs_handle_t *);
|
||||
extern const char *libzfs_error_description(libzfs_handle_t *);
|
||||
extern int zfs_standard_error(libzfs_handle_t *, int, const char *);
|
||||
extern void libzfs_mnttab_init(libzfs_handle_t *);
|
||||
extern void libzfs_mnttab_fini(libzfs_handle_t *);
|
||||
extern void libzfs_mnttab_cache(libzfs_handle_t *, boolean_t);
|
||||
@ -537,6 +538,7 @@ extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_snapshots(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
|
||||
extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_bookmarks(zfs_handle_t *, zfs_iter_f, void *);
|
||||
|
||||
typedef struct get_all_cb {
|
||||
zfs_handle_t **cb_handles;
|
||||
@ -611,6 +613,7 @@ typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
|
||||
|
||||
extern int zfs_send(zfs_handle_t *, const char *, const char *,
|
||||
sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
|
||||
extern int zfs_send_one(zfs_handle_t *, const char *, int);
|
||||
|
||||
extern int zfs_promote(zfs_handle_t *);
|
||||
extern int zfs_hold(zfs_handle_t *, const char *, const char *,
|
||||
@ -680,6 +683,7 @@ extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
|
||||
extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
|
||||
zfs_type_t);
|
||||
extern int zfs_spa_version(zfs_handle_t *, int *);
|
||||
extern boolean_t zfs_bookmark_exists(const char *path);
|
||||
|
||||
/*
|
||||
* Mount support functions.
|
||||
|
@ -295,7 +295,7 @@ zpool_handle(zfs_handle_t *zhp)
|
||||
int len;
|
||||
zpool_handle_t *zph;
|
||||
|
||||
len = strcspn(zhp->zfs_name, "/@") + 1;
|
||||
len = strcspn(zhp->zfs_name, "/@#") + 1;
|
||||
pool_name = zfs_alloc(zhp->zfs_hdl, len);
|
||||
(void) strlcpy(pool_name, zhp->zfs_name, len);
|
||||
|
||||
@ -579,6 +579,70 @@ zfs_handle_dup(zfs_handle_t *zhp_orig)
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
zfs_bookmark_exists(const char *path)
|
||||
{
|
||||
nvlist_t *bmarks;
|
||||
nvlist_t *props;
|
||||
char fsname[ZFS_MAXNAMELEN];
|
||||
char *bmark_name;
|
||||
char *pound;
|
||||
int err;
|
||||
boolean_t rv;
|
||||
|
||||
|
||||
(void) strlcpy(fsname, path, sizeof (fsname));
|
||||
pound = strchr(fsname, '#');
|
||||
if (pound == NULL)
|
||||
return (B_FALSE);
|
||||
|
||||
*pound = '\0';
|
||||
bmark_name = pound + 1;
|
||||
props = fnvlist_alloc();
|
||||
err = lzc_get_bookmarks(fsname, props, &bmarks);
|
||||
nvlist_free(props);
|
||||
if (err != 0) {
|
||||
nvlist_free(bmarks);
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
rv = nvlist_exists(bmarks, bmark_name);
|
||||
nvlist_free(bmarks);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
zfs_handle_t *
|
||||
make_bookmark_handle(zfs_handle_t *parent, const char *path,
|
||||
nvlist_t *bmark_props)
|
||||
{
|
||||
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
|
||||
|
||||
if (zhp == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* Fill in the name. */
|
||||
zhp->zfs_hdl = parent->zfs_hdl;
|
||||
(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
|
||||
|
||||
/* Set the property lists. */
|
||||
if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) {
|
||||
free(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Set the types. */
|
||||
zhp->zfs_head_type = parent->zfs_head_type;
|
||||
zhp->zfs_type = ZFS_TYPE_BOOKMARK;
|
||||
|
||||
if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) {
|
||||
nvlist_free(zhp->zfs_props);
|
||||
free(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens the given snapshot, filesystem, or volume. The 'types'
|
||||
* argument is a mask of acceptable types. The function will print an
|
||||
@ -2271,6 +2335,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
|
||||
case ZFS_TYPE_SNAPSHOT:
|
||||
str = "snapshot";
|
||||
break;
|
||||
case ZFS_TYPE_BOOKMARK:
|
||||
str = "bookmark";
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
@ -3133,6 +3200,19 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
|
||||
nvlist_t *nv = fnvlist_alloc();
|
||||
fnvlist_add_boolean(nv, zhp->zfs_name);
|
||||
int error = lzc_destroy_bookmarks(nv, NULL);
|
||||
fnvlist_free(nv);
|
||||
if (error != 0) {
|
||||
return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
|
||||
dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
|
||||
zhp->zfs_name));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
if (ZFS_IS_VOLUME(zhp)) {
|
||||
@ -3515,45 +3595,44 @@ typedef struct rollback_data {
|
||||
const char *cb_target; /* the snapshot */
|
||||
uint64_t cb_create; /* creation time reference */
|
||||
boolean_t cb_error;
|
||||
boolean_t cb_dependent;
|
||||
boolean_t cb_force;
|
||||
} rollback_data_t;
|
||||
|
||||
static int
|
||||
rollback_destroy_dependent(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
rollback_data_t *cbp = data;
|
||||
prop_changelist_t *clp;
|
||||
|
||||
/* We must destroy this clone; first unmount it */
|
||||
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
|
||||
cbp->cb_force ? MS_FORCE: 0);
|
||||
if (clp == NULL || changelist_prefix(clp) != 0) {
|
||||
cbp->cb_error = B_TRUE;
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
if (zfs_destroy(zhp, B_FALSE) != 0)
|
||||
cbp->cb_error = B_TRUE;
|
||||
else
|
||||
changelist_remove(clp, zhp->zfs_name);
|
||||
(void) changelist_postfix(clp);
|
||||
changelist_free(clp);
|
||||
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
rollback_destroy(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
rollback_data_t *cbp = data;
|
||||
|
||||
if (!cbp->cb_dependent) {
|
||||
if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
|
||||
zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
|
||||
cbp->cb_create) {
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
|
||||
cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
|
||||
rollback_destroy_dependent, cbp);
|
||||
|
||||
cbp->cb_dependent = B_TRUE;
|
||||
cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
|
||||
rollback_destroy, cbp);
|
||||
cbp->cb_dependent = B_FALSE;
|
||||
|
||||
cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
|
||||
}
|
||||
} else {
|
||||
/* We must destroy this clone; first unmount it */
|
||||
prop_changelist_t *clp;
|
||||
|
||||
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
|
||||
cbp->cb_force ? MS_FORCE: 0);
|
||||
if (clp == NULL || changelist_prefix(clp) != 0) {
|
||||
cbp->cb_error = B_TRUE;
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
if (zfs_destroy(zhp, B_FALSE) != 0)
|
||||
cbp->cb_error = B_TRUE;
|
||||
else
|
||||
changelist_remove(clp, zhp->zfs_name);
|
||||
(void) changelist_postfix(clp);
|
||||
changelist_free(clp);
|
||||
cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
|
||||
}
|
||||
|
||||
zfs_close(zhp);
|
||||
@ -3564,8 +3643,8 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
|
||||
* Given a dataset, rollback to a specific snapshot, discarding any
|
||||
* data changes since then and making it the active dataset.
|
||||
*
|
||||
* Any snapshots more recent than the target are destroyed, along with
|
||||
* their dependents.
|
||||
* Any snapshots and bookmarks more recent than the target are
|
||||
* destroyed, along with their dependents (i.e. clones).
|
||||
*/
|
||||
int
|
||||
zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
||||
@ -3585,7 +3664,8 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
||||
cb.cb_force = force;
|
||||
cb.cb_target = snap->zfs_name;
|
||||
cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
|
||||
(void) zfs_iter_children(zhp, rollback_destroy, &cb);
|
||||
(void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb);
|
||||
(void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb);
|
||||
|
||||
if (cb.cb_error)
|
||||
return (-1);
|
||||
|
@ -23,7 +23,7 @@
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
||||
*/
|
||||
|
||||
@ -191,6 +191,8 @@ int create_parents(libzfs_handle_t *, char *, int);
|
||||
boolean_t isa_child_of(const char *dataset, const char *parent);
|
||||
|
||||
zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
|
||||
zfs_handle_t *make_bookmark_handle(zfs_handle_t *, const char *,
|
||||
nvlist_t *props);
|
||||
|
||||
int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
||||
* All rights reserved.
|
||||
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
|
||||
@ -146,7 +146,8 @@ zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
|
||||
zhp->zfs_type == ZFS_TYPE_BOOKMARK)
|
||||
return (0);
|
||||
|
||||
zc.zc_simple = simple;
|
||||
@ -172,6 +173,59 @@ zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all bookmarks
|
||||
*/
|
||||
int
|
||||
zfs_iter_bookmarks(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_handle_t *nzhp;
|
||||
nvlist_t *props = NULL;
|
||||
nvlist_t *bmarks = NULL;
|
||||
int err;
|
||||
|
||||
if ((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) != 0)
|
||||
return (0);
|
||||
|
||||
/* Setup the requested properties nvlist. */
|
||||
props = fnvlist_alloc();
|
||||
fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_GUID));
|
||||
fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
||||
fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATION));
|
||||
|
||||
/* Allocate an nvlist to hold the bookmarks. */
|
||||
bmarks = fnvlist_alloc();
|
||||
|
||||
if ((err = lzc_get_bookmarks(zhp->zfs_name, props, &bmarks)) != 0)
|
||||
goto out;
|
||||
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(bmarks, pair)) {
|
||||
char name[ZFS_MAXNAMELEN];
|
||||
char *bmark_name;
|
||||
nvlist_t *bmark_props;
|
||||
|
||||
bmark_name = nvpair_name(pair);
|
||||
bmark_props = fnvpair_value_nvlist(pair);
|
||||
|
||||
(void) snprintf(name, sizeof (name), "%s#%s", zhp->zfs_name,
|
||||
bmark_name);
|
||||
|
||||
nzhp = make_bookmark_handle(zhp, name, bmark_props);
|
||||
if (nzhp == NULL)
|
||||
continue;
|
||||
|
||||
if ((err = func(nzhp, data)) != 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
fnvlist_free(props);
|
||||
fnvlist_free(bmarks);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines for dealing with the sorted snapshot functionality
|
||||
*/
|
||||
@ -406,13 +460,13 @@ static int
|
||||
iter_dependents_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
iter_dependents_arg_t *ida = arg;
|
||||
int err;
|
||||
int err = 0;
|
||||
boolean_t first = ida->first;
|
||||
ida->first = B_FALSE;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
|
||||
err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
|
||||
} else {
|
||||
} else if (zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
|
||||
iter_stack_frame_t isf;
|
||||
iter_stack_frame_t *f;
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
||||
* All rights reserved.
|
||||
@ -1619,6 +1619,62 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_send_one(zfs_handle_t *zhp, const char *from, int fd)
|
||||
{
|
||||
int err;
|
||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||
|
||||
char errbuf[1024];
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"warning: cannot send '%s'"), zhp->zfs_name);
|
||||
|
||||
err = lzc_send(zhp->zfs_name, from, fd);
|
||||
if (err != 0) {
|
||||
switch (errno) {
|
||||
case EXDEV:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"not an earlier snapshot from the same fs"));
|
||||
return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
|
||||
|
||||
case ENOENT:
|
||||
case ESRCH:
|
||||
if (lzc_exists(zhp->zfs_name)) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"incremental source (%s) does not exist"),
|
||||
from);
|
||||
}
|
||||
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
||||
|
||||
case EBUSY:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"target is busy; if a filesystem, "
|
||||
"it must not be mounted"));
|
||||
return (zfs_error(hdl, EZFS_BUSY, errbuf));
|
||||
|
||||
case EDQUOT:
|
||||
case EFBIG:
|
||||
case EIO:
|
||||
case ENOLINK:
|
||||
case ENOSPC:
|
||||
#ifdef illumos
|
||||
case ENOSTR:
|
||||
#endif
|
||||
case ENXIO:
|
||||
case EPIPE:
|
||||
case ERANGE:
|
||||
case EFAULT:
|
||||
case EROFS:
|
||||
zfs_error_aux(hdl, strerror(errno));
|
||||
return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
|
||||
|
||||
default:
|
||||
return (zfs_standard_error(hdl, errno, errbuf));
|
||||
}
|
||||
}
|
||||
return (err != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines specific to "zfs recv"
|
||||
*/
|
||||
|
@ -486,18 +486,30 @@ lzc_get_holds(const char *snapname, nvlist_t **holdsp)
|
||||
}
|
||||
|
||||
/*
|
||||
* If fromsnap is NULL, a full (non-incremental) stream will be sent.
|
||||
*
|
||||
* "snapname" is the full name of the snapshot to send (e.g. "pool/fs@snap")
|
||||
*
|
||||
* If "from" is NULL, a full (non-incremental) stream will be sent.
|
||||
* If "from" is non-NULL, it must be the full name of a snapshot or
|
||||
* bookmark to send an incremental from (e.g. "pool/fs@earlier_snap" or
|
||||
* "pool/fs#earlier_bmark"). If non-NULL, the specified snapshot or
|
||||
* bookmark must represent an earlier point in the history of "snapname").
|
||||
* It can be an earlier snapshot in the same filesystem or zvol as "snapname",
|
||||
* or it can be the origin of "snapname"'s filesystem, or an earlier
|
||||
* snapshot in the origin, etc.
|
||||
*
|
||||
* "fd" is the file descriptor to write the send stream to.
|
||||
*/
|
||||
int
|
||||
lzc_send(const char *snapname, const char *fromsnap, int fd)
|
||||
lzc_send(const char *snapname, const char *from, int fd)
|
||||
{
|
||||
nvlist_t *args;
|
||||
int err;
|
||||
|
||||
args = fnvlist_alloc();
|
||||
fnvlist_add_int32(args, "fd", fd);
|
||||
if (fromsnap != NULL)
|
||||
fnvlist_add_string(args, "fromsnap", fromsnap);
|
||||
if (from != NULL)
|
||||
fnvlist_add_string(args, "fromsnap", from);
|
||||
err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
|
||||
nvlist_free(args);
|
||||
return (err);
|
||||
@ -652,3 +664,97 @@ lzc_rollback(const char *fsname, char *snapnamebuf, int snapnamelen)
|
||||
}
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates bookmarks.
|
||||
*
|
||||
* The bookmarks nvlist maps from name of the bookmark (e.g. "pool/fs#bmark") to
|
||||
* the name of the snapshot (e.g. "pool/fs@snap"). All the bookmarks and
|
||||
* snapshots must be in the same pool.
|
||||
*
|
||||
* The returned results nvlist will have an entry for each bookmark that failed.
|
||||
* The value will be the (int32) error code.
|
||||
*
|
||||
* The return value will be 0 if all bookmarks were created, otherwise it will
|
||||
* be the errno of a (undetermined) bookmarks that failed.
|
||||
*/
|
||||
int
|
||||
lzc_bookmark(nvlist_t *bookmarks, nvlist_t **errlist)
|
||||
{
|
||||
nvpair_t *elem;
|
||||
int error;
|
||||
char pool[MAXNAMELEN];
|
||||
|
||||
/* determine the pool name */
|
||||
elem = nvlist_next_nvpair(bookmarks, NULL);
|
||||
if (elem == NULL)
|
||||
return (0);
|
||||
(void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
|
||||
pool[strcspn(pool, "/#")] = '\0';
|
||||
|
||||
error = lzc_ioctl(ZFS_IOC_BOOKMARK, pool, bookmarks, errlist);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve bookmarks.
|
||||
*
|
||||
* Retrieve the list of bookmarks for the given file system. The props
|
||||
* parameter is an nvlist of property names (with no values) that will be
|
||||
* returned for each bookmark.
|
||||
*
|
||||
* The following are valid properties on bookmarks, all of which are numbers
|
||||
* (represented as uint64 in the nvlist)
|
||||
*
|
||||
* "guid" - globally unique identifier of the snapshot it refers to
|
||||
* "createtxg" - txg when the snapshot it refers to was created
|
||||
* "creation" - timestamp when the snapshot it refers to was created
|
||||
*
|
||||
* The format of the returned nvlist as follows:
|
||||
* <short name of bookmark> -> {
|
||||
* <name of property> -> {
|
||||
* "value" -> uint64
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
int
|
||||
lzc_get_bookmarks(const char *fsname, nvlist_t *props, nvlist_t **bmarks)
|
||||
{
|
||||
return (lzc_ioctl(ZFS_IOC_GET_BOOKMARKS, fsname, props, bmarks));
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys bookmarks.
|
||||
*
|
||||
* The keys in the bmarks nvlist are the bookmarks to be destroyed.
|
||||
* They must all be in the same pool. Bookmarks are specified as
|
||||
* <fs>#<bmark>.
|
||||
*
|
||||
* Bookmarks that do not exist will be silently ignored.
|
||||
*
|
||||
* The return value will be 0 if all bookmarks that existed were destroyed.
|
||||
*
|
||||
* Otherwise the return value will be the errno of a (undetermined) bookmark
|
||||
* that failed, no bookmarks will be destroyed, and the errlist will have an
|
||||
* entry for each bookmarks that failed. The value in the errlist will be
|
||||
* the (int32) error code.
|
||||
*/
|
||||
int
|
||||
lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist)
|
||||
{
|
||||
nvpair_t *elem;
|
||||
int error;
|
||||
char pool[MAXNAMELEN];
|
||||
|
||||
/* determine the pool name */
|
||||
elem = nvlist_next_nvpair(bmarks, NULL);
|
||||
if (elem == NULL)
|
||||
return (0);
|
||||
(void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
|
||||
pool[strcspn(pool, "/#")] = '\0';
|
||||
|
||||
error = lzc_ioctl(ZFS_IOC_DESTROY_BOOKMARKS, pool, bmarks, errlist);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
@ -39,27 +39,27 @@ extern "C" {
|
||||
int libzfs_core_init(void);
|
||||
void libzfs_core_fini(void);
|
||||
|
||||
int lzc_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t **errlist);
|
||||
int lzc_create(const char *fsname, dmu_objset_type_t type, nvlist_t *props);
|
||||
int lzc_clone(const char *fsname, const char *origin, nvlist_t *props);
|
||||
int lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist);
|
||||
int lzc_snapshot(nvlist_t *, nvlist_t *, nvlist_t **);
|
||||
int lzc_create(const char *, dmu_objset_type_t, nvlist_t *);
|
||||
int lzc_clone(const char *, const char *, nvlist_t *);
|
||||
int lzc_destroy_snaps(nvlist_t *, boolean_t, nvlist_t **);
|
||||
int lzc_bookmark(nvlist_t *, nvlist_t **);
|
||||
int lzc_get_bookmarks(const char *, nvlist_t *, nvlist_t **);
|
||||
int lzc_destroy_bookmarks(nvlist_t *, nvlist_t **);
|
||||
|
||||
int lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
|
||||
uint64_t *usedp);
|
||||
int lzc_snaprange_space(const char *, const char *, uint64_t *);
|
||||
|
||||
int lzc_hold(nvlist_t *holds, int cleanup_fd, nvlist_t **errlist);
|
||||
int lzc_release(nvlist_t *holds, nvlist_t **errlist);
|
||||
int lzc_get_holds(const char *snapname, nvlist_t **holdsp);
|
||||
int lzc_hold(nvlist_t *, int, nvlist_t **);
|
||||
int lzc_release(nvlist_t *, nvlist_t **);
|
||||
int lzc_get_holds(const char *, nvlist_t **);
|
||||
|
||||
int lzc_send(const char *snapname, const char *fromsnap, int fd);
|
||||
int lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, int fd);
|
||||
int lzc_send_space(const char *snapname, const char *fromsnap,
|
||||
uint64_t *result);
|
||||
int lzc_send(const char *, const char *, int);
|
||||
int lzc_receive(const char *, nvlist_t *, const char *, boolean_t, int);
|
||||
int lzc_send_space(const char *, const char *, uint64_t *);
|
||||
|
||||
boolean_t lzc_exists(const char *dataset);
|
||||
boolean_t lzc_exists(const char *);
|
||||
|
||||
int lzc_rollback(const char *fsname, char *snapnamebuf, int snapnamelen);
|
||||
int lzc_rollback(const char *, char *, int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
# CDDL HEADER END
|
||||
#
|
||||
# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
#
|
||||
|
||||
"""This module implements the "zfs allow" and "zfs unallow" subcommands.
|
||||
@ -219,6 +220,7 @@ def decodeid(w, toidfunc, fmt):
|
||||
hold=_("Allows adding a user hold to a snapshot"),
|
||||
release=_("Allows releasing a user hold which\n\t\t\t\tmight destroy the snapshot"),
|
||||
diff=_("Allows lookup of paths within a dataset,\n\t\t\t\tgiven an object number. Ordinary users need this\n\t\t\t\tin order to use zfs diff"),
|
||||
bookmark="",
|
||||
)
|
||||
|
||||
perms_other = dict(
|
||||
|
@ -196,4 +196,13 @@ zpool_feature_init(void)
|
||||
"com.delphix:extensible_dataset", "extensible_dataset",
|
||||
"Enhanced dataset functionality, used by other features.",
|
||||
B_FALSE, B_FALSE, B_FALSE, NULL);
|
||||
|
||||
static const spa_feature_t bookmarks_deps[] = {
|
||||
SPA_FEATURE_EXTENSIBLE_DATASET,
|
||||
SPA_FEATURE_NONE
|
||||
};
|
||||
zfeature_register(SPA_FEATURE_BOOKMARKS,
|
||||
"com.delphix:bookmarks", "bookmarks",
|
||||
"\"zfs bookmark\" command",
|
||||
B_TRUE, B_FALSE, B_FALSE, bookmarks_deps);
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ typedef enum spa_feature {
|
||||
SPA_FEATURE_ENABLED_TXG,
|
||||
SPA_FEATURE_HOLE_BIRTH,
|
||||
SPA_FEATURE_EXTENSIBLE_DATASET,
|
||||
SPA_FEATURE_BOOKMARKS,
|
||||
SPA_FEATURES
|
||||
} spa_feature_t;
|
||||
|
||||
|
@ -21,8 +21,11 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sunddi.h>
|
||||
@ -34,43 +37,34 @@
|
||||
#include <libnvpair.h>
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
/* XXX includes zfs_context.h, so why bother with the above? */
|
||||
#include <sys/dsl_deleg.h>
|
||||
#include "zfs_prop.h"
|
||||
#include "zfs_deleg.h"
|
||||
#include "zfs_namecheck.h"
|
||||
|
||||
/*
|
||||
* permission table
|
||||
*
|
||||
* Keep this table in sorted order
|
||||
*
|
||||
* This table is used for displaying all permissions for
|
||||
* zfs allow
|
||||
*/
|
||||
|
||||
zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = {
|
||||
{ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW},
|
||||
{ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
|
||||
{ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
|
||||
{ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
|
||||
{ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
|
||||
{ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
|
||||
{ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
|
||||
{ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
|
||||
{ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
|
||||
{ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
|
||||
{ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
|
||||
{ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
|
||||
{ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
|
||||
{ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
|
||||
{ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
|
||||
{ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
|
||||
{ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
|
||||
{ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
|
||||
{ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
|
||||
{ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
|
||||
{NULL, ZFS_DELEG_NOTE_NONE }
|
||||
{ZFS_DELEG_PERM_ALLOW},
|
||||
{ZFS_DELEG_PERM_BOOKMARK},
|
||||
{ZFS_DELEG_PERM_CLONE},
|
||||
{ZFS_DELEG_PERM_CREATE},
|
||||
{ZFS_DELEG_PERM_DESTROY},
|
||||
{ZFS_DELEG_PERM_DIFF},
|
||||
{ZFS_DELEG_PERM_MOUNT},
|
||||
{ZFS_DELEG_PERM_PROMOTE},
|
||||
{ZFS_DELEG_PERM_RECEIVE},
|
||||
{ZFS_DELEG_PERM_RENAME},
|
||||
{ZFS_DELEG_PERM_ROLLBACK},
|
||||
{ZFS_DELEG_PERM_SNAPSHOT},
|
||||
{ZFS_DELEG_PERM_SHARE},
|
||||
{ZFS_DELEG_PERM_SEND},
|
||||
{ZFS_DELEG_PERM_USERPROP},
|
||||
{ZFS_DELEG_PERM_USERQUOTA},
|
||||
{ZFS_DELEG_PERM_GROUPQUOTA},
|
||||
{ZFS_DELEG_PERM_USERUSED},
|
||||
{ZFS_DELEG_PERM_GROUPUSED},
|
||||
{ZFS_DELEG_PERM_HOLD},
|
||||
{ZFS_DELEG_PERM_RELEASE},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static int
|
||||
|
@ -21,6 +21,7 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ZFS_DELEG_H
|
||||
@ -65,6 +66,7 @@ typedef enum {
|
||||
ZFS_DELEG_NOTE_HOLD,
|
||||
ZFS_DELEG_NOTE_RELEASE,
|
||||
ZFS_DELEG_NOTE_DIFF,
|
||||
ZFS_DELEG_NOTE_BOOKMARK,
|
||||
ZFS_DELEG_NOTE_NONE
|
||||
} zfs_deleg_note_t;
|
||||
|
||||
|
@ -744,7 +744,7 @@ zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t * innvl, const int vec,
|
||||
* allocate and populate nvlist with recursive
|
||||
* snapshots
|
||||
*/
|
||||
if (snapshot_namecheck(zc->zc_value, NULL,
|
||||
if (zfs_component_namecheck(zc->zc_value, NULL,
|
||||
NULL) == 0) {
|
||||
tmpnvl = fnvlist_alloc();
|
||||
if (dmu_get_recursive_snaps_nvl(zc->zc_name,
|
||||
|
@ -22,6 +22,9 @@
|
||||
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Common name validation routines for ZFS. These routines are shared by the
|
||||
@ -62,7 +65,7 @@ valid_char(char c)
|
||||
* [-_.: ]
|
||||
*/
|
||||
int
|
||||
snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||
zfs_component_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||
{
|
||||
const char *loc;
|
||||
|
||||
@ -113,7 +116,7 @@ permset_namecheck(const char *path, namecheck_err_t *why, char *what)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (snapshot_namecheck(&path[1], why, what));
|
||||
return (zfs_component_namecheck(&path[1], why, what));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -22,6 +22,9 @@
|
||||
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ZFS_NAMECHECK_H
|
||||
#define _ZFS_NAMECHECK_H
|
||||
@ -48,7 +51,7 @@ typedef enum {
|
||||
int pool_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 snapshot_namecheck(const char *, namecheck_err_t *, char *);
|
||||
int zfs_component_namecheck(const char *, namecheck_err_t *, char *);
|
||||
int permset_namecheck(const char *, namecheck_err_t *, char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
@ -312,7 +312,8 @@ zfs_prop_init(void)
|
||||
PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off | share(1M) options",
|
||||
"SHARENFS");
|
||||
zprop_register_string(ZFS_PROP_TYPE, "type", NULL, PROP_READONLY,
|
||||
ZFS_TYPE_DATASET, "filesystem | volume | snapshot", "TYPE");
|
||||
ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK,
|
||||
"filesystem | volume | snapshot | bookmark", "TYPE");
|
||||
zprop_register_string(ZFS_PROP_SHARESMB, "sharesmb", "off",
|
||||
PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
|
||||
"on | off | sharemgr(1M) options", "SHARESMB");
|
||||
@ -378,18 +379,18 @@ zfs_prop_init(void)
|
||||
|
||||
/* hidden properties */
|
||||
zprop_register_hidden(ZFS_PROP_CREATETXG, "createtxg", PROP_TYPE_NUMBER,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, "CREATETXG");
|
||||
PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "CREATETXG");
|
||||
zprop_register_hidden(ZFS_PROP_NUMCLONES, "numclones", PROP_TYPE_NUMBER,
|
||||
PROP_READONLY, ZFS_TYPE_SNAPSHOT, "NUMCLONES");
|
||||
zprop_register_hidden(ZFS_PROP_NAME, "name", PROP_TYPE_STRING,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, "NAME");
|
||||
PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "NAME");
|
||||
zprop_register_hidden(ZFS_PROP_ISCSIOPTIONS, "iscsioptions",
|
||||
PROP_TYPE_STRING, PROP_INHERIT, ZFS_TYPE_VOLUME, "ISCSIOPTIONS");
|
||||
zprop_register_hidden(ZFS_PROP_STMF_SHAREINFO, "stmf_sbd_lu",
|
||||
PROP_TYPE_STRING, PROP_INHERIT, ZFS_TYPE_VOLUME,
|
||||
"STMF_SBD_LU");
|
||||
zprop_register_hidden(ZFS_PROP_GUID, "guid", PROP_TYPE_NUMBER,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, "GUID");
|
||||
PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "GUID");
|
||||
zprop_register_hidden(ZFS_PROP_USERACCOUNTING, "useraccounting",
|
||||
PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET,
|
||||
"USERACCOUNTING");
|
||||
@ -402,7 +403,7 @@ zfs_prop_init(void)
|
||||
|
||||
/* oddball properties */
|
||||
zprop_register_impl(ZFS_PROP_CREATION, "creation", PROP_TYPE_NUMBER, 0,
|
||||
NULL, PROP_READONLY, ZFS_TYPE_DATASET,
|
||||
NULL, PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK,
|
||||
"<date>", "CREATION", B_FALSE, B_TRUE, NULL);
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ ZFS_COMMON_OBJS += \
|
||||
dmu_tx.o \
|
||||
dnode.o \
|
||||
dnode_sync.o \
|
||||
dsl_bookmark.o \
|
||||
dsl_dir.o \
|
||||
dsl_dataset.o \
|
||||
dsl_deadlist.o \
|
||||
|
@ -214,7 +214,7 @@ dmu_diff(const char *tosnap_name, const char *fromsnap_name,
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (!dsl_dataset_is_before(tosnap, fromsnap)) {
|
||||
if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) {
|
||||
dsl_dataset_rele(fromsnap, FTAG);
|
||||
dsl_dataset_rele(tosnap, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include <sys/zfs_onexit.h>
|
||||
#include <sys/dmu_send.h>
|
||||
#include <sys/dsl_destroy.h>
|
||||
#include <sys/dsl_bookmark.h>
|
||||
|
||||
/* Set this tunable to TRUE to replace corrupt data with 0x2f5baddb10c */
|
||||
int zfs_send_corrupt_data = B_FALSE;
|
||||
@ -372,6 +373,12 @@ backup_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
if (zb->zb_object != DMU_META_DNODE_OBJECT &&
|
||||
DMU_OBJECT_IS_SPECIAL(zb->zb_object)) {
|
||||
return (0);
|
||||
} else if (zb->zb_level == ZB_ZIL_LEVEL) {
|
||||
/*
|
||||
* If we are sending a non-snapshot (which is allowed on
|
||||
* read-only pools), it may have a ZIL, which must be ignored.
|
||||
*/
|
||||
return (0);
|
||||
} else if (BP_IS_HOLE(bp) &&
|
||||
zb->zb_object == DMU_META_DNODE_OBJECT) {
|
||||
uint64_t span = BP_SPAN(dnp, zb->zb_level);
|
||||
@ -420,6 +427,7 @@ backup_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
arc_buf_t *abuf;
|
||||
int blksz = BP_GET_LSIZE(bp);
|
||||
|
||||
ASSERT0(zb->zb_level);
|
||||
if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf,
|
||||
ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL,
|
||||
&aflags, zb) != 0) {
|
||||
@ -447,14 +455,16 @@ backup_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
}
|
||||
|
||||
/*
|
||||
* Releases dp, ds, and fromds, using the specified tag.
|
||||
* Releases dp using the specified tag.
|
||||
*/
|
||||
static int
|
||||
dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
|
||||
#ifdef illumos
|
||||
dsl_dataset_t *fromds, int outfd, vnode_t *vp, offset_t *off)
|
||||
zfs_bookmark_phys_t *fromzb, boolean_t is_clone, int outfd,
|
||||
vnode_t *vp, offset_t *off)
|
||||
#else
|
||||
dsl_dataset_t *fromds, int outfd, struct file *fp, offset_t *off)
|
||||
zfs_bookmark_phys_t *fromzb, boolean_t is_clone, int outfd,
|
||||
struct file *fp, offset_t *off)
|
||||
#endif
|
||||
{
|
||||
objset_t *os;
|
||||
@ -463,18 +473,8 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
|
||||
int err;
|
||||
uint64_t fromtxg = 0;
|
||||
|
||||
if (fromds != NULL && !dsl_dataset_is_before(ds, fromds)) {
|
||||
dsl_dataset_rele(fromds, tag);
|
||||
dsl_dataset_rele(ds, tag);
|
||||
dsl_pool_rele(dp, tag);
|
||||
return (SET_ERROR(EXDEV));
|
||||
}
|
||||
|
||||
err = dmu_objset_from_ds(ds, &os);
|
||||
if (err != 0) {
|
||||
if (fromds != NULL)
|
||||
dsl_dataset_rele(fromds, tag);
|
||||
dsl_dataset_rele(ds, tag);
|
||||
dsl_pool_rele(dp, tag);
|
||||
return (err);
|
||||
}
|
||||
@ -490,9 +490,6 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
|
||||
uint64_t version;
|
||||
if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &version) != 0) {
|
||||
kmem_free(drr, sizeof (dmu_replay_record_t));
|
||||
if (fromds != NULL)
|
||||
dsl_dataset_rele(fromds, tag);
|
||||
dsl_dataset_rele(ds, tag);
|
||||
dsl_pool_rele(dp, tag);
|
||||
return (SET_ERROR(EINVAL));
|
||||
}
|
||||
@ -507,20 +504,20 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
|
||||
drr->drr_u.drr_begin.drr_creation_time =
|
||||
ds->ds_phys->ds_creation_time;
|
||||
drr->drr_u.drr_begin.drr_type = dmu_objset_type(os);
|
||||
if (fromds != NULL && ds->ds_dir != fromds->ds_dir)
|
||||
if (is_clone)
|
||||
drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CLONE;
|
||||
drr->drr_u.drr_begin.drr_toguid = ds->ds_phys->ds_guid;
|
||||
if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
|
||||
drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CI_DATA;
|
||||
|
||||
if (fromds != NULL)
|
||||
drr->drr_u.drr_begin.drr_fromguid = fromds->ds_phys->ds_guid;
|
||||
if (fromzb != NULL) {
|
||||
drr->drr_u.drr_begin.drr_fromguid = fromzb->zbm_guid;
|
||||
fromtxg = fromzb->zbm_creation_txg;
|
||||
}
|
||||
dsl_dataset_name(ds, drr->drr_u.drr_begin.drr_toname);
|
||||
|
||||
if (fromds != NULL) {
|
||||
fromtxg = fromds->ds_phys->ds_creation_txg;
|
||||
dsl_dataset_rele(fromds, tag);
|
||||
fromds = NULL;
|
||||
if (!dsl_dataset_is_snapshot(ds)) {
|
||||
(void) strlcat(drr->drr_u.drr_begin.drr_toname, "@--head--",
|
||||
sizeof (drr->drr_u.drr_begin.drr_toname));
|
||||
}
|
||||
|
||||
dsp = kmem_zalloc(sizeof (dmu_sendarg_t), KM_SLEEP);
|
||||
@ -535,7 +532,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
|
||||
dsp->dsa_toguid = ds->ds_phys->ds_guid;
|
||||
ZIO_SET_CHECKSUM(&dsp->dsa_zc, 0, 0, 0, 0);
|
||||
dsp->dsa_pending_op = PENDING_NONE;
|
||||
dsp->dsa_incremental = (fromtxg != 0);
|
||||
dsp->dsa_incremental = (fromzb != NULL);
|
||||
|
||||
mutex_enter(&ds->ds_sendstream_lock);
|
||||
list_insert_head(&ds->ds_sendstreams, dsp);
|
||||
@ -581,7 +578,6 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
|
||||
kmem_free(dsp, sizeof (dmu_sendarg_t));
|
||||
|
||||
dsl_dataset_long_rele(ds, FTAG);
|
||||
dsl_dataset_rele(ds, tag);
|
||||
|
||||
return (err);
|
||||
}
|
||||
@ -610,15 +606,30 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap,
|
||||
}
|
||||
|
||||
if (fromsnap != 0) {
|
||||
zfs_bookmark_phys_t zb;
|
||||
boolean_t is_clone;
|
||||
|
||||
err = dsl_dataset_hold_obj(dp, fromsnap, FTAG, &fromds);
|
||||
if (err != 0) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (err);
|
||||
}
|
||||
if (!dsl_dataset_is_before(ds, fromds, 0))
|
||||
err = SET_ERROR(EXDEV);
|
||||
zb.zbm_creation_time = fromds->ds_phys->ds_creation_time;
|
||||
zb.zbm_creation_txg = fromds->ds_phys->ds_creation_txg;
|
||||
zb.zbm_guid = fromds->ds_phys->ds_guid;
|
||||
is_clone = (fromds->ds_dir != ds->ds_dir);
|
||||
dsl_dataset_rele(fromds, FTAG);
|
||||
err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone,
|
||||
outfd, fp, off);
|
||||
} else {
|
||||
err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE,
|
||||
outfd, fp, off);
|
||||
}
|
||||
|
||||
return (dmu_send_impl(FTAG, dp, ds, fromds, outfd, fp, off));
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
@ -631,33 +642,79 @@ dmu_send(const char *tosnap, const char *fromsnap,
|
||||
{
|
||||
dsl_pool_t *dp;
|
||||
dsl_dataset_t *ds;
|
||||
dsl_dataset_t *fromds = NULL;
|
||||
int err;
|
||||
boolean_t owned = B_FALSE;
|
||||
|
||||
if (strchr(tosnap, '@') == NULL)
|
||||
return (SET_ERROR(EINVAL));
|
||||
if (fromsnap != NULL && strchr(fromsnap, '@') == NULL)
|
||||
if (fromsnap != NULL && strpbrk(fromsnap, "@#") == NULL)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
err = dsl_pool_hold(tosnap, FTAG, &dp);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
err = dsl_dataset_hold(dp, tosnap, FTAG, &ds);
|
||||
if (strchr(tosnap, '@') == NULL && spa_writeable(dp->dp_spa)) {
|
||||
/*
|
||||
* We are sending a filesystem or volume. Ensure
|
||||
* that it doesn't change by owning the dataset.
|
||||
*/
|
||||
err = dsl_dataset_own(dp, tosnap, FTAG, &ds);
|
||||
owned = B_TRUE;
|
||||
} else {
|
||||
err = dsl_dataset_hold(dp, tosnap, FTAG, &ds);
|
||||
}
|
||||
if (err != 0) {
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (fromsnap != NULL) {
|
||||
err = dsl_dataset_hold(dp, fromsnap, FTAG, &fromds);
|
||||
zfs_bookmark_phys_t zb;
|
||||
boolean_t is_clone = B_FALSE;
|
||||
int fsnamelen = strchr(tosnap, '@') - tosnap;
|
||||
|
||||
/*
|
||||
* If the fromsnap is in a different filesystem, then
|
||||
* mark the send stream as a clone.
|
||||
*/
|
||||
if (strncmp(tosnap, fromsnap, fsnamelen) != 0 ||
|
||||
(fromsnap[fsnamelen] != '@' &&
|
||||
fromsnap[fsnamelen] != '#')) {
|
||||
is_clone = B_TRUE;
|
||||
}
|
||||
|
||||
if (strchr(fromsnap, '@')) {
|
||||
dsl_dataset_t *fromds;
|
||||
err = dsl_dataset_hold(dp, fromsnap, FTAG, &fromds);
|
||||
if (err == 0) {
|
||||
if (!dsl_dataset_is_before(ds, fromds, 0))
|
||||
err = SET_ERROR(EXDEV);
|
||||
zb.zbm_creation_time =
|
||||
fromds->ds_phys->ds_creation_time;
|
||||
zb.zbm_creation_txg =
|
||||
fromds->ds_phys->ds_creation_txg;
|
||||
zb.zbm_guid = fromds->ds_phys->ds_guid;
|
||||
is_clone = (ds->ds_dir != fromds->ds_dir);
|
||||
dsl_dataset_rele(fromds, FTAG);
|
||||
}
|
||||
} else {
|
||||
err = dsl_bookmark_lookup(dp, fromsnap, ds, &zb);
|
||||
}
|
||||
if (err != 0) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (err);
|
||||
}
|
||||
err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone,
|
||||
outfd, fp, off);
|
||||
} else {
|
||||
err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE,
|
||||
outfd, fp, off);
|
||||
}
|
||||
return (dmu_send_impl(FTAG, dp, ds, fromds, outfd, fp, off));
|
||||
if (owned)
|
||||
dsl_dataset_disown(ds, FTAG);
|
||||
else
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
@ -677,7 +734,7 @@ dmu_send_estimate(dsl_dataset_t *ds, dsl_dataset_t *fromds, uint64_t *sizep)
|
||||
* fromsnap must be an earlier snapshot from the same fs as tosnap,
|
||||
* or the origin's fs.
|
||||
*/
|
||||
if (fromds != NULL && !dsl_dataset_is_before(ds, fromds))
|
||||
if (fromds != NULL && !dsl_dataset_is_before(ds, fromds, 0))
|
||||
return (SET_ERROR(EXDEV));
|
||||
|
||||
/* Get uncompressed size estimate of changed data. */
|
||||
|
454
sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
Normal file
454
sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
Normal file
@ -0,0 +1,454 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/dsl_dataset.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
#include <sys/dsl_prop.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/dmu_impl.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/arc.h>
|
||||
#include <sys/zap.h>
|
||||
#include <sys/zfeature.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/dsl_bookmark.h>
|
||||
#include <zfs_namecheck.h>
|
||||
|
||||
static int
|
||||
dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname,
|
||||
dsl_dataset_t **dsp, void *tag, char **shortnamep)
|
||||
{
|
||||
char buf[MAXNAMELEN];
|
||||
char *hashp;
|
||||
|
||||
if (strlen(fullname) >= MAXNAMELEN)
|
||||
return (SET_ERROR(ENAMETOOLONG));
|
||||
hashp = strchr(fullname, '#');
|
||||
if (hashp == NULL)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
*shortnamep = hashp + 1;
|
||||
if (zfs_component_namecheck(*shortnamep, NULL, NULL))
|
||||
return (SET_ERROR(EINVAL));
|
||||
(void) strlcpy(buf, fullname, hashp - fullname + 1);
|
||||
return (dsl_dataset_hold(dp, buf, tag, dsp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns ESRCH if bookmark is not found.
|
||||
*/
|
||||
static int
|
||||
dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname,
|
||||
zfs_bookmark_phys_t *bmark_phys)
|
||||
{
|
||||
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
|
||||
uint64_t bmark_zapobj = ds->ds_bookmarks;
|
||||
matchtype_t mt;
|
||||
int err;
|
||||
|
||||
if (bmark_zapobj == 0)
|
||||
return (SET_ERROR(ESRCH));
|
||||
|
||||
if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
|
||||
mt = MT_FIRST;
|
||||
else
|
||||
mt = MT_EXACT;
|
||||
|
||||
err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t),
|
||||
sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt,
|
||||
NULL, 0, NULL);
|
||||
|
||||
return (err == ENOENT ? ESRCH : err);
|
||||
}
|
||||
|
||||
/*
|
||||
* If later_ds is non-NULL, this will return EXDEV if the the specified bookmark
|
||||
* does not represents an earlier point in later_ds's timeline.
|
||||
*
|
||||
* Returns ENOENT if the dataset containing the bookmark does not exist.
|
||||
* Returns ESRCH if the dataset exists but the bookmark was not found in it.
|
||||
*/
|
||||
int
|
||||
dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname,
|
||||
dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp)
|
||||
{
|
||||
char *shortname;
|
||||
dsl_dataset_t *ds;
|
||||
int error;
|
||||
|
||||
error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
error = dsl_dataset_bmark_lookup(ds, shortname, bmp);
|
||||
if (error == 0 && later_ds != NULL) {
|
||||
if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg))
|
||||
error = SET_ERROR(EXDEV);
|
||||
}
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (error);
|
||||
}
|
||||
|
||||
typedef struct dsl_bookmark_create_arg {
|
||||
nvlist_t *dbca_bmarks;
|
||||
nvlist_t *dbca_errors;
|
||||
} dsl_bookmark_create_arg_t;
|
||||
|
||||
static int
|
||||
dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name,
|
||||
dmu_tx_t *tx)
|
||||
{
|
||||
dsl_pool_t *dp = dmu_tx_pool(tx);
|
||||
dsl_dataset_t *bmark_fs;
|
||||
char *shortname;
|
||||
int error;
|
||||
zfs_bookmark_phys_t bmark_phys;
|
||||
|
||||
if (!dsl_dataset_is_snapshot(snapds))
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
error = dsl_bookmark_hold_ds(dp, bookmark_name,
|
||||
&bmark_fs, FTAG, &shortname);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) {
|
||||
dsl_dataset_rele(bmark_fs, FTAG);
|
||||
return (SET_ERROR(EINVAL));
|
||||
}
|
||||
|
||||
error = dsl_dataset_bmark_lookup(bmark_fs, shortname,
|
||||
&bmark_phys);
|
||||
dsl_dataset_rele(bmark_fs, FTAG);
|
||||
if (error == 0)
|
||||
return (SET_ERROR(EEXIST));
|
||||
if (error == ESRCH)
|
||||
return (0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
dsl_bookmark_create_check(void *arg, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_bookmark_create_arg_t *dbca = arg;
|
||||
dsl_pool_t *dp = dmu_tx_pool(tx);
|
||||
int rv = 0;
|
||||
|
||||
if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
|
||||
return (SET_ERROR(ENOTSUP));
|
||||
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
|
||||
dsl_dataset_t *snapds;
|
||||
int error;
|
||||
|
||||
/* note: validity of nvlist checked by ioctl layer */
|
||||
error = dsl_dataset_hold(dp, fnvpair_value_string(pair),
|
||||
FTAG, &snapds);
|
||||
if (error == 0) {
|
||||
error = dsl_bookmark_create_check_impl(snapds,
|
||||
nvpair_name(pair), tx);
|
||||
dsl_dataset_rele(snapds, FTAG);
|
||||
}
|
||||
if (error != 0) {
|
||||
fnvlist_add_int32(dbca->dbca_errors,
|
||||
nvpair_name(pair), error);
|
||||
rv = error;
|
||||
}
|
||||
}
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static void
|
||||
dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_bookmark_create_arg_t *dbca = arg;
|
||||
dsl_pool_t *dp = dmu_tx_pool(tx);
|
||||
objset_t *mos = dp->dp_meta_objset;
|
||||
|
||||
ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
|
||||
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
|
||||
dsl_dataset_t *snapds, *bmark_fs;
|
||||
zfs_bookmark_phys_t bmark_phys;
|
||||
char *shortname;
|
||||
|
||||
VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair),
|
||||
FTAG, &snapds));
|
||||
VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
|
||||
&bmark_fs, FTAG, &shortname));
|
||||
if (bmark_fs->ds_bookmarks == 0) {
|
||||
bmark_fs->ds_bookmarks =
|
||||
zap_create_norm(mos, U8_TEXTPREP_TOUPPER,
|
||||
DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
|
||||
spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
|
||||
|
||||
dsl_dataset_zapify(bmark_fs, tx);
|
||||
VERIFY0(zap_add(mos, bmark_fs->ds_object,
|
||||
DS_FIELD_BOOKMARK_NAMES,
|
||||
sizeof (bmark_fs->ds_bookmarks), 1,
|
||||
&bmark_fs->ds_bookmarks, tx));
|
||||
}
|
||||
|
||||
bmark_phys.zbm_guid = snapds->ds_phys->ds_guid;
|
||||
bmark_phys.zbm_creation_txg = snapds->ds_phys->ds_creation_txg;
|
||||
bmark_phys.zbm_creation_time =
|
||||
snapds->ds_phys->ds_creation_time;
|
||||
|
||||
VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
|
||||
shortname, sizeof (uint64_t),
|
||||
sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t),
|
||||
&bmark_phys, tx));
|
||||
|
||||
spa_history_log_internal_ds(bmark_fs, "bookmark", tx,
|
||||
"name=%s creation_txg=%llu target_snap=%llu",
|
||||
shortname,
|
||||
(longlong_t)bmark_phys.zbm_creation_txg,
|
||||
(longlong_t)snapds->ds_object);
|
||||
|
||||
dsl_dataset_rele(bmark_fs, FTAG);
|
||||
dsl_dataset_rele(snapds, FTAG);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The bookmarks must all be in the same pool.
|
||||
*/
|
||||
int
|
||||
dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors)
|
||||
{
|
||||
nvpair_t *pair;
|
||||
dsl_bookmark_create_arg_t dbca;
|
||||
|
||||
pair = nvlist_next_nvpair(bmarks, NULL);
|
||||
if (pair == NULL)
|
||||
return (0);
|
||||
|
||||
dbca.dbca_bmarks = bmarks;
|
||||
dbca.dbca_errors = errors;
|
||||
|
||||
return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check,
|
||||
dsl_bookmark_create_sync, &dbca, fnvlist_num_pairs(bmarks)));
|
||||
}
|
||||
|
||||
int
|
||||
dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
|
||||
{
|
||||
int err = 0;
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t attr;
|
||||
dsl_pool_t *dp = ds->ds_dir->dd_pool;
|
||||
|
||||
uint64_t bmark_zapobj = ds->ds_bookmarks;
|
||||
if (bmark_zapobj == 0)
|
||||
return (0);
|
||||
|
||||
for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj);
|
||||
zap_cursor_retrieve(&zc, &attr) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
char *bmark_name = attr.za_name;
|
||||
zfs_bookmark_phys_t bmark_phys;
|
||||
|
||||
err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
|
||||
ASSERT3U(err, !=, ENOENT);
|
||||
if (err != 0)
|
||||
break;
|
||||
|
||||
nvlist_t *out_props = fnvlist_alloc();
|
||||
if (nvlist_exists(props,
|
||||
zfs_prop_to_name(ZFS_PROP_GUID))) {
|
||||
dsl_prop_nvlist_add_uint64(out_props,
|
||||
ZFS_PROP_GUID, bmark_phys.zbm_guid);
|
||||
}
|
||||
if (nvlist_exists(props,
|
||||
zfs_prop_to_name(ZFS_PROP_CREATETXG))) {
|
||||
dsl_prop_nvlist_add_uint64(out_props,
|
||||
ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg);
|
||||
}
|
||||
if (nvlist_exists(props,
|
||||
zfs_prop_to_name(ZFS_PROP_CREATION))) {
|
||||
dsl_prop_nvlist_add_uint64(out_props,
|
||||
ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
|
||||
}
|
||||
|
||||
fnvlist_add_nvlist(outnvl, bmark_name, out_props);
|
||||
fnvlist_free(out_props);
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the bookmarks that exist in the specified dataset, and the
|
||||
* requested properties of each bookmark.
|
||||
*
|
||||
* The "props" nvlist specifies which properties are requested.
|
||||
* See lzc_get_bookmarks() for the list of valid properties.
|
||||
*/
|
||||
int
|
||||
dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl)
|
||||
{
|
||||
dsl_pool_t *dp;
|
||||
dsl_dataset_t *ds;
|
||||
int err;
|
||||
|
||||
err = dsl_pool_hold(dsname, FTAG, &dp);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
|
||||
if (err != 0) {
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
err = dsl_get_bookmarks_impl(ds, props, outnvl);
|
||||
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
typedef struct dsl_bookmark_destroy_arg {
|
||||
nvlist_t *dbda_bmarks;
|
||||
nvlist_t *dbda_success;
|
||||
nvlist_t *dbda_errors;
|
||||
} dsl_bookmark_destroy_arg_t;
|
||||
|
||||
static int
|
||||
dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
|
||||
{
|
||||
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
|
||||
uint64_t bmark_zapobj = ds->ds_bookmarks;
|
||||
matchtype_t mt;
|
||||
|
||||
if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
|
||||
mt = MT_FIRST;
|
||||
else
|
||||
mt = MT_EXACT;
|
||||
|
||||
return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
|
||||
}
|
||||
|
||||
static int
|
||||
dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_bookmark_destroy_arg_t *dbda = arg;
|
||||
dsl_pool_t *dp = dmu_tx_pool(tx);
|
||||
int rv = 0;
|
||||
|
||||
if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
|
||||
return (0);
|
||||
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
|
||||
const char *fullname = nvpair_name(pair);
|
||||
dsl_dataset_t *ds;
|
||||
zfs_bookmark_phys_t bm;
|
||||
int error;
|
||||
char *shortname;
|
||||
|
||||
error = dsl_bookmark_hold_ds(dp, fullname, &ds,
|
||||
FTAG, &shortname);
|
||||
if (error == ENOENT) {
|
||||
/* ignore it; the bookmark is "already destroyed" */
|
||||
continue;
|
||||
}
|
||||
if (error == 0) {
|
||||
error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
if (error == ESRCH) {
|
||||
/*
|
||||
* ignore it; the bookmark is
|
||||
* "already destroyed"
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (error == 0) {
|
||||
fnvlist_add_boolean(dbda->dbda_success, fullname);
|
||||
} else {
|
||||
fnvlist_add_int32(dbda->dbda_errors, fullname, error);
|
||||
rv = error;
|
||||
}
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
||||
static void
|
||||
dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
|
||||
{
|
||||
dsl_bookmark_destroy_arg_t *dbda = arg;
|
||||
dsl_pool_t *dp = dmu_tx_pool(tx);
|
||||
objset_t *mos = dp->dp_meta_objset;
|
||||
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
|
||||
dsl_dataset_t *ds;
|
||||
char *shortname;
|
||||
uint64_t zap_cnt;
|
||||
|
||||
VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
|
||||
&ds, FTAG, &shortname));
|
||||
VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
|
||||
|
||||
/*
|
||||
* If all of this dataset's bookmarks have been destroyed,
|
||||
* free the zap object and decrement the feature's use count.
|
||||
*/
|
||||
VERIFY0(zap_count(mos, ds->ds_bookmarks,
|
||||
&zap_cnt));
|
||||
if (zap_cnt == 0) {
|
||||
dmu_buf_will_dirty(ds->ds_dbuf, tx);
|
||||
VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
|
||||
ds->ds_bookmarks = 0;
|
||||
spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
|
||||
VERIFY0(zap_remove(mos, ds->ds_object,
|
||||
DS_FIELD_BOOKMARK_NAMES, tx));
|
||||
}
|
||||
|
||||
spa_history_log_internal_ds(ds, "remove bookmark", tx,
|
||||
"name=%s", shortname);
|
||||
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The bookmarks must all be in the same pool.
|
||||
*/
|
||||
int
|
||||
dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
|
||||
{
|
||||
int rv;
|
||||
dsl_bookmark_destroy_arg_t dbda;
|
||||
nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
|
||||
if (pair == NULL)
|
||||
return (0);
|
||||
|
||||
dbda.dbda_bmarks = bmarks;
|
||||
dbda.dbda_errors = errors;
|
||||
dbda.dbda_success = fnvlist_alloc();
|
||||
|
||||
rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
|
||||
dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks));
|
||||
fnvlist_free(dbda.dbda_success);
|
||||
return (rv);
|
||||
}
|
@ -48,6 +48,7 @@
|
||||
#include <sys/dsl_deadlist.h>
|
||||
#include <sys/dsl_destroy.h>
|
||||
#include <sys/dsl_userhold.h>
|
||||
#include <sys/dsl_bookmark.h>
|
||||
|
||||
#define SWITCH64(x, y) \
|
||||
{ \
|
||||
@ -405,6 +406,14 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||
ds->ds_phys->ds_prev_snap_obj,
|
||||
ds, &ds->ds_prev);
|
||||
}
|
||||
if (doi.doi_type == DMU_OTN_ZAP_METADATA) {
|
||||
int zaperr = zap_lookup(mos, ds->ds_object,
|
||||
DS_FIELD_BOOKMARK_NAMES,
|
||||
sizeof (ds->ds_bookmarks), 1,
|
||||
&ds->ds_bookmarks);
|
||||
if (zaperr != ENOENT)
|
||||
VERIFY0(zaperr);
|
||||
}
|
||||
} else {
|
||||
if (zfs_flags & ZFS_DEBUG_SNAPNAMES)
|
||||
err = dsl_dataset_get_snapname(ds);
|
||||
@ -1801,6 +1810,28 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
|
||||
return (SET_ERROR(EINVAL));
|
||||
}
|
||||
|
||||
/* must not have any bookmarks after the most recent snapshot */
|
||||
nvlist_t *proprequest = fnvlist_alloc();
|
||||
fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
||||
nvlist_t *bookmarks = fnvlist_alloc();
|
||||
error = dsl_get_bookmarks_impl(ds, proprequest, bookmarks);
|
||||
fnvlist_free(proprequest);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(bookmarks, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(bookmarks, pair)) {
|
||||
nvlist_t *valuenv =
|
||||
fnvlist_lookup_nvlist(fnvpair_value_nvlist(pair),
|
||||
zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
||||
uint64_t createtxg = fnvlist_lookup_uint64(valuenv, "value");
|
||||
if (createtxg > ds->ds_phys->ds_prev_snap_txg) {
|
||||
fnvlist_free(bookmarks);
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (SET_ERROR(EEXIST));
|
||||
}
|
||||
}
|
||||
fnvlist_free(bookmarks);
|
||||
|
||||
error = dsl_dataset_handoff_check(ds, ddra->ddra_owner, tx);
|
||||
if (error != 0) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
@ -3022,18 +3053,25 @@ dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap,
|
||||
* 'earlier' is before 'later'. Or 'earlier' could be the origin of
|
||||
* 'later's filesystem. Or 'earlier' could be an older snapshot in the origin's
|
||||
* filesystem. Or 'earlier' could be the origin's origin.
|
||||
*
|
||||
* If non-zero, earlier_txg is used instead of earlier's ds_creation_txg.
|
||||
*/
|
||||
boolean_t
|
||||
dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier)
|
||||
dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier,
|
||||
uint64_t earlier_txg)
|
||||
{
|
||||
dsl_pool_t *dp = later->ds_dir->dd_pool;
|
||||
int error;
|
||||
boolean_t ret;
|
||||
|
||||
ASSERT(dsl_pool_config_held(dp));
|
||||
ASSERT(dsl_dataset_is_snapshot(earlier) || earlier_txg != 0);
|
||||
|
||||
if (earlier->ds_phys->ds_creation_txg >=
|
||||
later->ds_phys->ds_creation_txg)
|
||||
if (earlier_txg == 0)
|
||||
earlier_txg = earlier->ds_phys->ds_creation_txg;
|
||||
|
||||
if (dsl_dataset_is_snapshot(later) &&
|
||||
earlier_txg >= later->ds_phys->ds_creation_txg)
|
||||
return (B_FALSE);
|
||||
|
||||
if (later->ds_dir == earlier->ds_dir)
|
||||
@ -3048,7 +3086,7 @@ dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier)
|
||||
later->ds_dir->dd_phys->dd_origin_obj, FTAG, &origin);
|
||||
if (error != 0)
|
||||
return (B_FALSE);
|
||||
ret = dsl_dataset_is_before(origin, earlier);
|
||||
ret = dsl_dataset_is_before(origin, earlier, earlier_txg);
|
||||
dsl_dataset_rele(origin, FTAG);
|
||||
return (ret);
|
||||
}
|
||||
|
@ -808,6 +808,12 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
|
||||
ASSERT(ds->ds_phys->ds_snapnames_zapobj != 0);
|
||||
VERIFY0(zap_destroy(mos, ds->ds_phys->ds_snapnames_zapobj, tx));
|
||||
|
||||
if (ds->ds_bookmarks != 0) {
|
||||
VERIFY0(zap_destroy(mos,
|
||||
ds->ds_bookmarks, tx));
|
||||
spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
|
||||
}
|
||||
|
||||
spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
|
||||
|
||||
ASSERT0(ds->ds_phys->ds_next_clones_obj);
|
||||
|
@ -481,7 +481,7 @@ spa_lookup(const char *name)
|
||||
* If it's a full dataset name, figure out the pool name and
|
||||
* just use that.
|
||||
*/
|
||||
cp = strpbrk(search.spa_name, "/@");
|
||||
cp = strpbrk(search.spa_name, "/@#");
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_DSL_BOOKMARK_H
|
||||
#define _SYS_DSL_BOOKMARK_H
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dsl_pool;
|
||||
struct dsl_dataset;
|
||||
|
||||
/*
|
||||
* On disk zap object.
|
||||
*/
|
||||
typedef struct zfs_bookmark_phys {
|
||||
uint64_t zbm_guid; /* guid of bookmarked dataset */
|
||||
uint64_t zbm_creation_txg; /* birth transaction group */
|
||||
uint64_t zbm_creation_time; /* bookmark creation time */
|
||||
} zfs_bookmark_phys_t;
|
||||
|
||||
int dsl_bookmark_create(nvlist_t *, nvlist_t *);
|
||||
int dsl_get_bookmarks(const char *, nvlist_t *, nvlist_t *);
|
||||
int dsl_get_bookmarks_impl(dsl_dataset_t *, nvlist_t *, nvlist_t *);
|
||||
int dsl_bookmark_destroy(nvlist_t *, nvlist_t *);
|
||||
int dsl_bookmark_lookup(struct dsl_pool *, const char *,
|
||||
struct dsl_dataset *, zfs_bookmark_phys_t *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_DSL_BOOKMARK_H */
|
@ -75,6 +75,13 @@ struct dsl_pool;
|
||||
* They should be of the format <reverse-dns>:<field>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This field's value is the object ID of a zap object which contains the
|
||||
* bookmarks of this dataset. If it is present, then this dataset is counted
|
||||
* in the refcount of the SPA_FEATURES_BOOKMARKS feature.
|
||||
*/
|
||||
#define DS_FIELD_BOOKMARK_NAMES "com.delphix:bookmarks"
|
||||
|
||||
/*
|
||||
* DS_FLAG_CI_DATASET is set if the dataset contains a file system whose
|
||||
* name lookups should be performed case-insensitively.
|
||||
@ -127,6 +134,7 @@ typedef struct dsl_dataset {
|
||||
|
||||
/* only used in syncing context, only valid for non-snapshots: */
|
||||
struct dsl_dataset *ds_prev;
|
||||
uint64_t ds_bookmarks; /* DMU_OTN_ZAP_METADATA */
|
||||
|
||||
/* has internal locking: */
|
||||
dsl_deadlist_t ds_deadlist;
|
||||
@ -247,7 +255,8 @@ int dsl_dataset_set_refquota(const char *dsname, zprop_source_t source,
|
||||
int dsl_dataset_set_refreservation(const char *dsname, zprop_source_t source,
|
||||
uint64_t reservation);
|
||||
|
||||
boolean_t dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier);
|
||||
boolean_t dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier,
|
||||
uint64_t earlier_txg);
|
||||
void dsl_dataset_long_hold(dsl_dataset_t *ds, void *tag);
|
||||
void dsl_dataset_long_rele(dsl_dataset_t *ds, void *tag);
|
||||
boolean_t dsl_dataset_long_held(dsl_dataset_t *ds);
|
||||
@ -270,6 +279,7 @@ int dsl_dataset_snap_lookup(dsl_dataset_t *ds, const char *name,
|
||||
int dsl_dataset_snap_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx);
|
||||
void dsl_dataset_set_refreservation_sync_impl(dsl_dataset_t *ds,
|
||||
zprop_source_t source, uint64_t value, dmu_tx_t *tx);
|
||||
void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx);
|
||||
int dsl_dataset_rollback(const char *fsname, void *owner, nvlist_t *result);
|
||||
|
||||
#ifdef ZFS_DEBUG
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_DSL_DELEG_H
|
||||
@ -56,6 +56,7 @@ extern "C" {
|
||||
#define ZFS_DELEG_PERM_HOLD "hold"
|
||||
#define ZFS_DELEG_PERM_RELEASE "release"
|
||||
#define ZFS_DELEG_PERM_DIFF "diff"
|
||||
#define ZFS_DELEG_PERM_BOOKMARK "bookmark"
|
||||
|
||||
/*
|
||||
* Note: the names of properties that are marked delegatable are also
|
||||
|
@ -643,7 +643,7 @@ zfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname)
|
||||
{
|
||||
objset_t *os = ((zfsvfs_t *)((vp)->v_vfsp->vfs_data))->z_os;
|
||||
|
||||
if (snapshot_namecheck(name, NULL, NULL) != 0)
|
||||
if (zfs_component_namecheck(name, NULL, NULL) != 0)
|
||||
return (SET_ERROR(EILSEQ));
|
||||
dmu_objset_name(os, zname);
|
||||
if (strlen(zname) + 1 + strlen(name) >= len)
|
||||
@ -884,7 +884,7 @@ zfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp,
|
||||
static enum symfollow follow = NO_FOLLOW;
|
||||
static enum uio_seg seg = UIO_SYSSPACE;
|
||||
|
||||
if (snapshot_namecheck(dirname, NULL, NULL) != 0)
|
||||
if (zfs_component_namecheck(dirname, NULL, NULL) != 0)
|
||||
return (SET_ERROR(EILSEQ));
|
||||
|
||||
dmu_objset_name(zfsvfs->z_os, name);
|
||||
|
@ -181,6 +181,7 @@
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dmu_send.h>
|
||||
#include <sys/dsl_destroy.h>
|
||||
#include <sys/dsl_bookmark.h>
|
||||
#include <sys/dsl_userhold.h>
|
||||
#include <sys/zfeature.h>
|
||||
|
||||
@ -847,22 +848,9 @@ zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
|
||||
return (SET_ERROR(EINVAL));
|
||||
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
|
||||
pair = nextpair) {
|
||||
dsl_pool_t *dp;
|
||||
dsl_dataset_t *ds;
|
||||
|
||||
error = dsl_pool_hold(nvpair_name(pair), FTAG, &dp);
|
||||
if (error != 0)
|
||||
break;
|
||||
nextpair = nvlist_next_nvpair(snaps, pair);
|
||||
error = dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds);
|
||||
if (error == 0)
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
|
||||
if (error == 0) {
|
||||
error = zfs_secpolicy_destroy_perms(nvpair_name(pair),
|
||||
cr);
|
||||
} else if (error == ENOENT) {
|
||||
error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr);
|
||||
if (error == ENOENT) {
|
||||
/*
|
||||
* Ignore any snapshots that don't exist (we consider
|
||||
* them "already destroyed"). Remove the name from the
|
||||
@ -1040,6 +1028,75 @@ zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for permission to create each snapshot in the nvlist.
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zfs_secpolicy_bookmark(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) {
|
||||
char *name = nvpair_name(pair);
|
||||
char *hashp = strchr(name, '#');
|
||||
|
||||
if (hashp == NULL) {
|
||||
error = SET_ERROR(EINVAL);
|
||||
break;
|
||||
}
|
||||
*hashp = '\0';
|
||||
error = zfs_secpolicy_write_perms(name,
|
||||
ZFS_DELEG_PERM_BOOKMARK, cr);
|
||||
*hashp = '#';
|
||||
if (error != 0)
|
||||
break;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zfs_secpolicy_destroy_bookmarks(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
|
||||
{
|
||||
nvpair_t *pair, *nextpair;
|
||||
int error = 0;
|
||||
|
||||
for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL;
|
||||
pair = nextpair) {
|
||||
char *name = nvpair_name(pair);
|
||||
char *hashp = strchr(name, '#');
|
||||
nextpair = nvlist_next_nvpair(innvl, pair);
|
||||
|
||||
if (hashp == NULL) {
|
||||
error = SET_ERROR(EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
*hashp = '\0';
|
||||
error = zfs_secpolicy_write_perms(name,
|
||||
ZFS_DELEG_PERM_DESTROY, cr);
|
||||
*hashp = '#';
|
||||
if (error == ENOENT) {
|
||||
/*
|
||||
* Ignore any filesystems that don't exist (we consider
|
||||
* their bookmarks "already destroyed"). Remove
|
||||
* the name from the nvl here in case the filesystem
|
||||
* is created between now and when we try to destroy
|
||||
* the bookmark (in which case we don't want to
|
||||
* destroy it since we haven't checked for permission).
|
||||
*/
|
||||
fnvlist_remove_nvpair(innvl, pair);
|
||||
error = 0;
|
||||
}
|
||||
if (error != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zfs_secpolicy_log_history(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
|
||||
@ -2637,7 +2694,6 @@ zfs_check_userprops(const char *fsname, nvlist_t *nvl)
|
||||
|
||||
while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
|
||||
const char *propname = nvpair_name(pair);
|
||||
char *valstr;
|
||||
|
||||
if (!zfs_prop_user(propname) ||
|
||||
nvpair_type(pair) != DATA_TYPE_STRING)
|
||||
@ -2650,8 +2706,7 @@ zfs_check_userprops(const char *fsname, nvlist_t *nvl)
|
||||
if (strlen(propname) >= ZAP_MAXNAMELEN)
|
||||
return (SET_ERROR(ENAMETOOLONG));
|
||||
|
||||
VERIFY(nvpair_value_string(pair, &valstr) == 0);
|
||||
if (strlen(valstr) >= ZAP_MAXVALUELEN)
|
||||
if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN)
|
||||
return (E2BIG);
|
||||
}
|
||||
return (0);
|
||||
@ -3336,7 +3391,8 @@ zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
* The snap name must contain an @, and the part after it must
|
||||
* contain only valid characters.
|
||||
*/
|
||||
if (cp == NULL || snapshot_namecheck(cp + 1, NULL, NULL) != 0)
|
||||
if (cp == NULL ||
|
||||
zfs_component_namecheck(cp + 1, NULL, NULL) != 0)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
/*
|
||||
@ -3489,10 +3545,10 @@ zfs_destroy_unmount_origin(const char *fsname)
|
||||
* outnvl: snapshot -> error code (int32)
|
||||
*
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
{
|
||||
int error, poollen;
|
||||
nvlist_t *snaps;
|
||||
nvpair_t *pair;
|
||||
boolean_t defer;
|
||||
@ -3501,27 +3557,109 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
return (SET_ERROR(EINVAL));
|
||||
defer = nvlist_exists(innvl, "defer");
|
||||
|
||||
poollen = strlen(poolname);
|
||||
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
|
||||
pair = nvlist_next_nvpair(snaps, pair)) {
|
||||
const char *name = nvpair_name(pair);
|
||||
|
||||
/*
|
||||
* The snap must be in the specified pool.
|
||||
*/
|
||||
if (strncmp(name, poolname, poollen) != 0 ||
|
||||
(name[poollen] != '/' && name[poollen] != '@'))
|
||||
return (SET_ERROR(EXDEV));
|
||||
|
||||
error = zfs_unmount_snap(name);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
(void) zvol_remove_minor(name);
|
||||
(void) zfs_unmount_snap(nvpair_name(pair));
|
||||
}
|
||||
|
||||
return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create bookmarks. Bookmark names are of the form <fs>#<bmark>.
|
||||
* All bookmarks must be in the same pool.
|
||||
*
|
||||
* innvl: {
|
||||
* bookmark1 -> snapshot1, bookmark2 -> snapshot2
|
||||
* }
|
||||
*
|
||||
* outnvl: bookmark -> error code (int32)
|
||||
*
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
{
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) {
|
||||
char *snap_name;
|
||||
|
||||
/*
|
||||
* Verify the snapshot argument.
|
||||
*/
|
||||
if (nvpair_value_string(pair, &snap_name) != 0)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
|
||||
/* Verify that the keys (bookmarks) are unique */
|
||||
for (nvpair_t *pair2 = nvlist_next_nvpair(innvl, pair);
|
||||
pair2 != NULL; pair2 = nvlist_next_nvpair(innvl, pair2)) {
|
||||
if (strcmp(nvpair_name(pair), nvpair_name(pair2)) == 0)
|
||||
return (SET_ERROR(EINVAL));
|
||||
}
|
||||
}
|
||||
|
||||
return (dsl_bookmark_create(innvl, outnvl));
|
||||
}
|
||||
|
||||
/*
|
||||
* innvl: {
|
||||
* property 1, property 2, ...
|
||||
* }
|
||||
*
|
||||
* outnvl: {
|
||||
* bookmark name 1 -> { property 1, property 2, ... },
|
||||
* bookmark name 2 -> { property 1, property 2, ... }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
static int
|
||||
zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
{
|
||||
return (dsl_get_bookmarks(fsname, innvl, outnvl));
|
||||
}
|
||||
|
||||
/*
|
||||
* innvl: {
|
||||
* bookmark name 1, bookmark name 2
|
||||
* }
|
||||
*
|
||||
* outnvl: bookmark -> error code (int32)
|
||||
*
|
||||
*/
|
||||
static int
|
||||
zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl,
|
||||
nvlist_t *outnvl)
|
||||
{
|
||||
int error, poollen;
|
||||
|
||||
poollen = strlen(poolname);
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) {
|
||||
const char *name = nvpair_name(pair);
|
||||
const char *cp = strchr(name, '#');
|
||||
|
||||
/*
|
||||
* The bookmark name must contain an #, and the part after it
|
||||
* must contain only valid characters.
|
||||
*/
|
||||
if (cp == NULL ||
|
||||
zfs_component_namecheck(cp + 1, NULL, NULL) != 0)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
/*
|
||||
* The bookmark must be in the specified pool.
|
||||
*/
|
||||
if (strncmp(name, poolname, poollen) != 0 ||
|
||||
(name[poollen] != '/' && name[poollen] != '#'))
|
||||
return (SET_ERROR(EXDEV));
|
||||
(void) zvol_remove_minor(name);
|
||||
}
|
||||
|
||||
error = dsl_bookmark_destroy(innvl, outnvl);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* inputs:
|
||||
* zc_name name of dataset to destroy
|
||||
@ -4196,7 +4334,8 @@ zfs_ioc_recv(zfs_cmd_t *zc)
|
||||
* zc_guid if set, estimate size of stream only. zc_cookie is ignored.
|
||||
* output size in zc_objset_type.
|
||||
*
|
||||
* outputs: none
|
||||
* outputs:
|
||||
* zc_objset_type estimated size, if zc_guid is set
|
||||
*/
|
||||
static int
|
||||
zfs_ioc_send(zfs_cmd_t *zc)
|
||||
@ -5459,6 +5598,19 @@ zfs_ioctl_init(void)
|
||||
zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME,
|
||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE);
|
||||
|
||||
zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK,
|
||||
zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME,
|
||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
|
||||
|
||||
zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS,
|
||||
zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME,
|
||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE);
|
||||
|
||||
zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS,
|
||||
zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks,
|
||||
POOL_NAME,
|
||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
|
||||
|
||||
/* IOCTLS that use the legacy function signature */
|
||||
|
||||
zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,
|
||||
@ -5894,7 +6046,7 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
|
||||
goto out;
|
||||
|
||||
/* legacy ioctls can modify zc_name */
|
||||
len = strcspn(zc->zc_name, "/@") + 1;
|
||||
len = strcspn(zc->zc_name, "/@#") + 1;
|
||||
saved_poolname = kmem_alloc(len, KM_SLEEP);
|
||||
(void) strlcpy(saved_poolname, zc->zc_name, len);
|
||||
|
||||
|
@ -49,10 +49,11 @@ extern "C" {
|
||||
* combined into masks that can be passed to various functions.
|
||||
*/
|
||||
typedef enum {
|
||||
ZFS_TYPE_FILESYSTEM = 0x1,
|
||||
ZFS_TYPE_SNAPSHOT = 0x2,
|
||||
ZFS_TYPE_VOLUME = 0x4,
|
||||
ZFS_TYPE_POOL = 0x8
|
||||
ZFS_TYPE_FILESYSTEM = (1 << 0),
|
||||
ZFS_TYPE_SNAPSHOT = (1 << 1),
|
||||
ZFS_TYPE_VOLUME = (1 << 2),
|
||||
ZFS_TYPE_POOL = (1 << 3),
|
||||
ZFS_TYPE_BOOKMARK = (1 << 4)
|
||||
} zfs_type_t;
|
||||
|
||||
typedef enum dmu_objset_type {
|
||||
@ -839,6 +840,9 @@ typedef enum zfs_ioc {
|
||||
ZFS_IOC_SEND_NEW,
|
||||
ZFS_IOC_SEND_SPACE,
|
||||
ZFS_IOC_CLONE,
|
||||
ZFS_IOC_BOOKMARK,
|
||||
ZFS_IOC_GET_BOOKMARKS,
|
||||
ZFS_IOC_DESTROY_BOOKMARKS,
|
||||
ZFS_IOC_LAST
|
||||
} zfs_ioc_t;
|
||||
|
||||
|
@ -191,6 +191,7 @@ cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c optional zfs compile-w
|
||||
cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c optional zfs compile-with "${ZFS_C}" \
|
||||
warning "kernel contains CDDL licensed ZFS filesystem"
|
||||
cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c optional zfs compile-with "${ZFS_C}"
|
||||
cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c optional zfs compile-with "${ZFS_C}"
|
||||
cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c optional zfs compile-with "${ZFS_C}"
|
||||
cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_deadlist.c optional zfs compile-with "${ZFS_C}"
|
||||
cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_deleg.c optional zfs compile-with "${ZFS_C}"
|
||||
|
Loading…
Reference in New Issue
Block a user