Update vendor-sys/illumos/dist to illumos-gate 13752:9f5f6c52ba19

(zfs part)

Obtained from:	ssh://anonhg@hg.illumos.org/illumos-gate
This commit is contained in:
Martin Matuska 2012-07-18 10:58:07 +00:00
parent 4eca1cfa73
commit 5b5bfb7422
18 changed files with 1066 additions and 337 deletions

View File

@ -57,6 +57,7 @@
#include <sys/arc.h> #include <sys/arc.h>
#include <sys/ddt.h> #include <sys/ddt.h>
#include <sys/zfeature.h> #include <sys/zfeature.h>
#include <zfs_comutil.h>
#undef ZFS_MAXNAMELEN #undef ZFS_MAXNAMELEN
#undef verify #undef verify
#include <libzfs.h> #include <libzfs.h>
@ -204,6 +205,27 @@ dump_packed_nvlist(objset_t *os, uint64_t object, void *data, size_t size)
nvlist_free(nv); nvlist_free(nv);
} }
/* ARGSUSED */
static void
dump_history_offsets(objset_t *os, uint64_t object, void *data, size_t size)
{
spa_history_phys_t *shp = data;
if (shp == NULL)
return;
(void) printf("\t\tpool_create_len = %llu\n",
(u_longlong_t)shp->sh_pool_create_len);
(void) printf("\t\tphys_max_off = %llu\n",
(u_longlong_t)shp->sh_phys_max_off);
(void) printf("\t\tbof = %llu\n",
(u_longlong_t)shp->sh_bof);
(void) printf("\t\teof = %llu\n",
(u_longlong_t)shp->sh_eof);
(void) printf("\t\trecords_lost = %llu\n",
(u_longlong_t)shp->sh_records_lost);
}
static void static void
zdb_nicenum(uint64_t num, char *buf) zdb_nicenum(uint64_t num, char *buf)
{ {
@ -853,21 +875,22 @@ dump_history(spa_t *spa)
for (int i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
uint64_t time, txg, ievent; uint64_t time, txg, ievent;
char *cmd, *intstr; char *cmd, *intstr;
boolean_t printed = B_FALSE;
if (nvlist_lookup_uint64(events[i], ZPOOL_HIST_TIME, if (nvlist_lookup_uint64(events[i], ZPOOL_HIST_TIME,
&time) != 0) &time) != 0)
continue; goto next;
if (nvlist_lookup_string(events[i], ZPOOL_HIST_CMD, if (nvlist_lookup_string(events[i], ZPOOL_HIST_CMD,
&cmd) != 0) { &cmd) != 0) {
if (nvlist_lookup_uint64(events[i], if (nvlist_lookup_uint64(events[i],
ZPOOL_HIST_INT_EVENT, &ievent) != 0) ZPOOL_HIST_INT_EVENT, &ievent) != 0)
continue; goto next;
verify(nvlist_lookup_uint64(events[i], verify(nvlist_lookup_uint64(events[i],
ZPOOL_HIST_TXG, &txg) == 0); ZPOOL_HIST_TXG, &txg) == 0);
verify(nvlist_lookup_string(events[i], verify(nvlist_lookup_string(events[i],
ZPOOL_HIST_INT_STR, &intstr) == 0); ZPOOL_HIST_INT_STR, &intstr) == 0);
if (ievent >= LOG_END) if (ievent >= ZFS_NUM_LEGACY_HISTORY_EVENTS)
continue; goto next;
(void) snprintf(internalstr, (void) snprintf(internalstr,
sizeof (internalstr), sizeof (internalstr),
@ -880,6 +903,14 @@ dump_history(spa_t *spa)
(void) localtime_r(&tsec, &t); (void) localtime_r(&tsec, &t);
(void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t); (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
(void) printf("%s %s\n", tbuf, cmd); (void) printf("%s %s\n", tbuf, cmd);
printed = B_TRUE;
next:
if (dump_opt['h'] > 1) {
if (!printed)
(void) printf("unrecognized record:\n");
dump_nvlist(events[i], 2);
}
} }
} }
@ -1456,7 +1487,7 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = {
dump_zap, /* other ZAP */ dump_zap, /* other ZAP */
dump_zap, /* persistent error log */ dump_zap, /* persistent error log */
dump_uint8, /* SPA history */ dump_uint8, /* SPA history */
dump_uint64, /* SPA history offsets */ dump_history_offsets, /* SPA history offsets */
dump_zap, /* Pool properties */ dump_zap, /* Pool properties */
dump_zap, /* DSL permissions */ dump_zap, /* DSL permissions */
dump_acl, /* ZFS ACL */ dump_acl, /* ZFS ACL */

View File

@ -56,6 +56,7 @@
#include <time.h> #include <time.h>
#include <libzfs.h> #include <libzfs.h>
#include <libzfs_core.h>
#include <zfs_prop.h> #include <zfs_prop.h>
#include <zfs_deleg.h> #include <zfs_deleg.h>
#include <libuutil.h> #include <libuutil.h>
@ -70,6 +71,7 @@ libzfs_handle_t *g_zfs;
static FILE *mnttab_file; static FILE *mnttab_file;
static char history_str[HIS_MAX_RECORD_LEN]; static char history_str[HIS_MAX_RECORD_LEN];
static boolean_t log_history = B_TRUE;
static int zfs_do_clone(int argc, char **argv); static int zfs_do_clone(int argc, char **argv);
static int zfs_do_create(int argc, char **argv); static int zfs_do_create(int argc, char **argv);
@ -259,7 +261,7 @@ get_usage(zfs_help_t idx)
return (gettext("\tshare <-a | filesystem>\n")); return (gettext("\tshare <-a | filesystem>\n"));
case HELP_SNAPSHOT: case HELP_SNAPSHOT:
return (gettext("\tsnapshot [-r] [-o property=value] ... " return (gettext("\tsnapshot [-r] [-o property=value] ... "
"<filesystem@snapname|volume@snapname>\n")); "<filesystem@snapname|volume@snapname> ...\n"));
case HELP_UNMOUNT: case HELP_UNMOUNT:
return (gettext("\tunmount [-f] " return (gettext("\tunmount [-f] "
"<-a | filesystem|mountpoint>\n")); "<-a | filesystem|mountpoint>\n"));
@ -888,9 +890,9 @@ typedef struct destroy_cbdata {
nvlist_t *cb_nvl; nvlist_t *cb_nvl;
/* first snap in contiguous run */ /* first snap in contiguous run */
zfs_handle_t *cb_firstsnap; char *cb_firstsnap;
/* previous snap in contiguous run */ /* previous snap in contiguous run */
zfs_handle_t *cb_prevsnap; char *cb_prevsnap;
int64_t cb_snapused; int64_t cb_snapused;
char *cb_snapspec; char *cb_snapspec;
} destroy_cbdata_t; } destroy_cbdata_t;
@ -1004,11 +1006,13 @@ destroy_print_cb(zfs_handle_t *zhp, void *arg)
if (nvlist_exists(cb->cb_nvl, name)) { if (nvlist_exists(cb->cb_nvl, name)) {
if (cb->cb_firstsnap == NULL) if (cb->cb_firstsnap == NULL)
cb->cb_firstsnap = zfs_handle_dup(zhp); cb->cb_firstsnap = strdup(name);
if (cb->cb_prevsnap != NULL) if (cb->cb_prevsnap != NULL)
zfs_close(cb->cb_prevsnap); free(cb->cb_prevsnap);
/* this snap continues the current range */ /* this snap continues the current range */
cb->cb_prevsnap = zfs_handle_dup(zhp); cb->cb_prevsnap = strdup(name);
if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
nomem();
if (cb->cb_verbose) { if (cb->cb_verbose) {
if (cb->cb_parsable) { if (cb->cb_parsable) {
(void) printf("destroy\t%s\n", name); (void) printf("destroy\t%s\n", name);
@ -1023,12 +1027,12 @@ destroy_print_cb(zfs_handle_t *zhp, void *arg)
} else if (cb->cb_firstsnap != NULL) { } else if (cb->cb_firstsnap != NULL) {
/* end of this range */ /* end of this range */
uint64_t used = 0; uint64_t used = 0;
err = zfs_get_snapused_int(cb->cb_firstsnap, err = lzc_snaprange_space(cb->cb_firstsnap,
cb->cb_prevsnap, &used); cb->cb_prevsnap, &used);
cb->cb_snapused += used; cb->cb_snapused += used;
zfs_close(cb->cb_firstsnap); free(cb->cb_firstsnap);
cb->cb_firstsnap = NULL; cb->cb_firstsnap = NULL;
zfs_close(cb->cb_prevsnap); free(cb->cb_prevsnap);
cb->cb_prevsnap = NULL; cb->cb_prevsnap = NULL;
} }
zfs_close(zhp); zfs_close(zhp);
@ -1045,13 +1049,13 @@ destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
if (cb->cb_firstsnap != NULL) { if (cb->cb_firstsnap != NULL) {
uint64_t used = 0; uint64_t used = 0;
if (err == 0) { if (err == 0) {
err = zfs_get_snapused_int(cb->cb_firstsnap, err = lzc_snaprange_space(cb->cb_firstsnap,
cb->cb_prevsnap, &used); cb->cb_prevsnap, &used);
} }
cb->cb_snapused += used; cb->cb_snapused += used;
zfs_close(cb->cb_firstsnap); free(cb->cb_firstsnap);
cb->cb_firstsnap = NULL; cb->cb_firstsnap = NULL;
zfs_close(cb->cb_prevsnap); free(cb->cb_prevsnap);
cb->cb_prevsnap = NULL; cb->cb_prevsnap = NULL;
} }
return (err); return (err);
@ -1064,7 +1068,7 @@ snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
int err = 0; int err = 0;
/* Check for clones. */ /* Check for clones. */
if (!cb->cb_doclones) { if (!cb->cb_doclones && !cb->cb_defer_destroy) {
cb->cb_target = zhp; cb->cb_target = zhp;
cb->cb_first = B_TRUE; cb->cb_first = B_TRUE;
err = zfs_iter_dependents(zhp, B_TRUE, err = zfs_iter_dependents(zhp, B_TRUE,
@ -1904,9 +1908,11 @@ upgrade_set_callback(zfs_handle_t *zhp, void *data)
/* /*
* If they did "zfs upgrade -a", then we could * If they did "zfs upgrade -a", then we could
* be doing ioctls to different pools. We need * be doing ioctls to different pools. We need
* to log this history once to each pool. * to log this history once to each pool, and bypass
* the normal history logging that happens in main().
*/ */
verify(zpool_stage_history(g_zfs, history_str) == 0); (void) zpool_log_history(g_zfs, history_str);
log_history = B_FALSE;
} }
if (zfs_prop_set(zhp, "version", verstr) == 0) if (zfs_prop_set(zhp, "version", verstr) == 0)
cb->cb_numupgraded++; cb->cb_numupgraded++;
@ -3424,6 +3430,32 @@ zfs_do_set(int argc, char **argv)
return (ret); return (ret);
} }
typedef struct snap_cbdata {
nvlist_t *sd_nvl;
boolean_t sd_recursive;
const char *sd_snapname;
} snap_cbdata_t;
static int
zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
{
snap_cbdata_t *sd = arg;
char *name;
int rv = 0;
int error;
error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
if (error == -1)
nomem();
fnvlist_add_boolean(sd->sd_nvl, name);
free(name);
if (sd->sd_recursive)
rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
zfs_close(zhp);
return (rv);
}
/* /*
* zfs snapshot [-r] [-o prop=value] ... <fs@snap> * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
* *
@ -3433,13 +3465,16 @@ zfs_do_set(int argc, char **argv)
static int static int
zfs_do_snapshot(int argc, char **argv) zfs_do_snapshot(int argc, char **argv)
{ {
boolean_t recursive = B_FALSE;
int ret = 0; int ret = 0;
char c; char c;
nvlist_t *props; nvlist_t *props;
snap_cbdata_t sd = { 0 };
boolean_t multiple_snaps = B_FALSE;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
nomem(); nomem();
if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
nomem();
/* check options */ /* check options */
while ((c = getopt(argc, argv, "ro:")) != -1) { while ((c = getopt(argc, argv, "ro:")) != -1) {
@ -3449,7 +3484,8 @@ zfs_do_snapshot(int argc, char **argv)
return (1); return (1);
break; break;
case 'r': case 'r':
recursive = B_TRUE; sd.sd_recursive = B_TRUE;
multiple_snaps = B_TRUE;
break; break;
case '?': case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"), (void) fprintf(stderr, gettext("invalid option '%c'\n"),
@ -3466,18 +3502,35 @@ zfs_do_snapshot(int argc, char **argv)
(void) fprintf(stderr, gettext("missing snapshot argument\n")); (void) fprintf(stderr, gettext("missing snapshot argument\n"));
goto usage; goto usage;
} }
if (argc > 1) {
(void) fprintf(stderr, gettext("too many arguments\n")); if (argc > 1)
goto usage; multiple_snaps = B_TRUE;
for (; argc > 0; argc--, argv++) {
char *atp;
zfs_handle_t *zhp;
atp = strchr(argv[0], '@');
if (atp == NULL)
goto usage;
*atp = '\0';
sd.sd_snapname = atp + 1;
zhp = zfs_open(g_zfs, argv[0],
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
if (zhp == NULL)
goto usage;
if (zfs_snapshot_cb(zhp, &sd) != 0)
goto usage;
} }
ret = zfs_snapshot(g_zfs, argv[0], recursive, props); ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
nvlist_free(sd.sd_nvl);
nvlist_free(props); nvlist_free(props);
if (ret && recursive) if (ret != 0 && multiple_snaps)
(void) fprintf(stderr, gettext("no snapshots were created\n")); (void) fprintf(stderr, gettext("no snapshots were created\n"));
return (ret != 0); return (ret != 0);
usage: usage:
nvlist_free(sd.sd_nvl);
nvlist_free(props); nvlist_free(props);
usage(B_FALSE); usage(B_FALSE);
return (-1); return (-1);
@ -6479,8 +6532,7 @@ main(int argc, char **argv)
return (1); return (1);
} }
zpool_set_history_str("zfs", argc, argv, history_str); zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
verify(zpool_stage_history(g_zfs, history_str) == 0);
libzfs_print_on_error(g_zfs, B_TRUE); libzfs_print_on_error(g_zfs, B_TRUE);
@ -6549,6 +6601,9 @@ main(int argc, char **argv)
(void) fclose(mnttab_file); (void) fclose(mnttab_file);
if (ret == 0 && log_history)
(void) zpool_log_history(g_zfs, history_str);
libzfs_fini(g_zfs); libzfs_fini(g_zfs);
/* /*

View File

@ -279,6 +279,9 @@ feature_enable_sync(void *arg1, void *arg2, dmu_tx_t *tx)
zfeature_info_t *feature = arg2; zfeature_info_t *feature = arg2;
spa_feature_enable(spa, feature, tx); spa_feature_enable(spa, feature, tx);
spa_history_log_internal(spa, "zhack enable feature", tx,
"name=%s can_readonly=%u",
feature->fi_guid, feature->fi_can_readonly);
} }
static void static void
@ -356,6 +359,8 @@ feature_incr_sync(void *arg1, void *arg2, dmu_tx_t *tx)
zfeature_info_t *feature = arg2; zfeature_info_t *feature = arg2;
spa_feature_incr(spa, feature, tx); spa_feature_incr(spa, feature, tx);
spa_history_log_internal(spa, "zhack feature incr", tx,
"name=%s", feature->fi_guid);
} }
static void static void
@ -365,6 +370,8 @@ feature_decr_sync(void *arg1, void *arg2, dmu_tx_t *tx)
zfeature_info_t *feature = arg2; zfeature_info_t *feature = arg2;
spa_feature_decr(spa, feature, tx); spa_feature_decr(spa, feature, tx);
spa_history_log_internal(spa, "zhack feature decr", tx,
"name=%s", feature->fi_guid);
} }
static void static void

View File

@ -185,9 +185,9 @@ static zpool_command_t command_table[] = {
#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
zpool_command_t *current_command; static zpool_command_t *current_command;
static char history_str[HIS_MAX_RECORD_LEN]; static char history_str[HIS_MAX_RECORD_LEN];
static boolean_t log_history = B_TRUE;
static uint_t timestamp_fmt = NODATE; static uint_t timestamp_fmt = NODATE;
static const char * static const char *
@ -935,7 +935,10 @@ zpool_do_destroy(int argc, char **argv)
return (1); return (1);
} }
ret = (zpool_destroy(zhp) != 0); /* The history must be logged as part of the export */
log_history = B_FALSE;
ret = (zpool_destroy(zhp, history_str) != 0);
zpool_close(zhp); zpool_close(zhp);
@ -999,10 +1002,13 @@ zpool_do_export(int argc, char **argv)
continue; continue;
} }
/* The history must be logged as part of the export */
log_history = B_FALSE;
if (hardforce) { if (hardforce) {
if (zpool_export_force(zhp) != 0) if (zpool_export_force(zhp, history_str) != 0)
ret = 1; ret = 1;
} else if (zpool_export(zhp, force) != 0) { } else if (zpool_export(zhp, force, history_str) != 0) {
ret = 1; ret = 1;
} }
@ -4269,6 +4275,14 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
(void) printf(gettext("Successfully upgraded " (void) printf(gettext("Successfully upgraded "
"'%s'\n\n"), zpool_get_name(zhp)); "'%s'\n\n"), zpool_get_name(zhp));
} }
/*
* If they did "zpool upgrade -a", then we could
* be doing ioctls to different pools. We need
* to log this history once to each pool, and bypass
* the normal history logging that happens in main().
*/
(void) zpool_log_history(g_zfs, history_str);
log_history = B_FALSE;
} }
} else if (cbp->cb_newer && !SPA_VERSION_IS_SUPPORTED(version)) { } else if (cbp->cb_newer && !SPA_VERSION_IS_SUPPORTED(version)) {
assert(!cbp->cb_all); assert(!cbp->cb_all);
@ -4491,8 +4505,8 @@ zpool_do_upgrade(int argc, char **argv)
typedef struct hist_cbdata { typedef struct hist_cbdata {
boolean_t first; boolean_t first;
int longfmt; boolean_t longfmt;
int internal; boolean_t internal;
} hist_cbdata_t; } hist_cbdata_t;
/* /*
@ -4504,21 +4518,8 @@ get_history_one(zpool_handle_t *zhp, void *data)
nvlist_t *nvhis; nvlist_t *nvhis;
nvlist_t **records; nvlist_t **records;
uint_t numrecords; uint_t numrecords;
char *cmdstr;
char *pathstr;
uint64_t dst_time;
time_t tsec;
struct tm t;
char tbuf[30];
int ret, i; int ret, i;
uint64_t who;
struct passwd *pwd;
char *hostname;
char *zonename;
char internalstr[MAXPATHLEN];
hist_cbdata_t *cb = (hist_cbdata_t *)data; hist_cbdata_t *cb = (hist_cbdata_t *)data;
uint64_t txg;
uint64_t ievent;
cb->first = B_FALSE; cb->first = B_FALSE;
@ -4530,64 +4531,94 @@ get_history_one(zpool_handle_t *zhp, void *data)
verify(nvlist_lookup_nvlist_array(nvhis, ZPOOL_HIST_RECORD, verify(nvlist_lookup_nvlist_array(nvhis, ZPOOL_HIST_RECORD,
&records, &numrecords) == 0); &records, &numrecords) == 0);
for (i = 0; i < numrecords; i++) { for (i = 0; i < numrecords; i++) {
if (nvlist_lookup_uint64(records[i], ZPOOL_HIST_TIME, nvlist_t *rec = records[i];
&dst_time) != 0) char tbuf[30] = "";
continue;
/* is it an internal event or a standard event? */ if (nvlist_exists(rec, ZPOOL_HIST_TIME)) {
if (nvlist_lookup_string(records[i], ZPOOL_HIST_CMD, time_t tsec;
&cmdstr) != 0) { struct tm t;
if (cb->internal == 0)
continue;
if (nvlist_lookup_uint64(records[i], tsec = fnvlist_lookup_uint64(records[i],
ZPOOL_HIST_INT_EVENT, &ievent) != 0) ZPOOL_HIST_TIME);
continue; (void) localtime_r(&tsec, &t);
verify(nvlist_lookup_uint64(records[i], (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
ZPOOL_HIST_TXG, &txg) == 0); }
verify(nvlist_lookup_string(records[i],
ZPOOL_HIST_INT_STR, &pathstr) == 0); if (nvlist_exists(rec, ZPOOL_HIST_CMD)) {
if (ievent >= LOG_END) (void) printf("%s %s", tbuf,
continue; fnvlist_lookup_string(rec, ZPOOL_HIST_CMD));
(void) snprintf(internalstr, } else if (nvlist_exists(rec, ZPOOL_HIST_INT_EVENT)) {
sizeof (internalstr), int ievent =
"[internal %s txg:%lld] %s", fnvlist_lookup_uint64(rec, ZPOOL_HIST_INT_EVENT);
zfs_history_event_names[ievent], txg, if (!cb->internal)
pathstr); continue;
cmdstr = internalstr; if (ievent >= ZFS_NUM_LEGACY_HISTORY_EVENTS) {
(void) printf("%s unrecognized record:\n",
tbuf);
dump_nvlist(rec, 4);
continue;
}
(void) printf("%s [internal %s txg:%lld] %s", tbuf,
zfs_history_event_names[ievent],
fnvlist_lookup_uint64(rec, ZPOOL_HIST_TXG),
fnvlist_lookup_string(rec, ZPOOL_HIST_INT_STR));
} else if (nvlist_exists(rec, ZPOOL_HIST_INT_NAME)) {
if (!cb->internal)
continue;
(void) printf("%s [txg:%lld] %s", tbuf,
fnvlist_lookup_uint64(rec, ZPOOL_HIST_TXG),
fnvlist_lookup_string(rec, ZPOOL_HIST_INT_NAME));
if (nvlist_exists(rec, ZPOOL_HIST_DSNAME)) {
(void) printf(" %s (%llu)",
fnvlist_lookup_string(rec,
ZPOOL_HIST_DSNAME),
fnvlist_lookup_uint64(rec,
ZPOOL_HIST_DSID));
}
(void) printf(" %s", fnvlist_lookup_string(rec,
ZPOOL_HIST_INT_STR));
} else if (nvlist_exists(rec, ZPOOL_HIST_IOCTL)) {
if (!cb->internal)
continue;
(void) printf("%s ioctl %s\n", tbuf,
fnvlist_lookup_string(rec, ZPOOL_HIST_IOCTL));
if (nvlist_exists(rec, ZPOOL_HIST_INPUT_NVL)) {
(void) printf(" input:\n");
dump_nvlist(fnvlist_lookup_nvlist(rec,
ZPOOL_HIST_INPUT_NVL), 8);
}
if (nvlist_exists(rec, ZPOOL_HIST_OUTPUT_NVL)) {
(void) printf(" output:\n");
dump_nvlist(fnvlist_lookup_nvlist(rec,
ZPOOL_HIST_OUTPUT_NVL), 8);
}
} else {
if (!cb->internal)
continue;
(void) printf("%s unrecognized record:\n", tbuf);
dump_nvlist(rec, 4);
} }
tsec = dst_time;
(void) localtime_r(&tsec, &t);
(void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
(void) printf("%s %s", tbuf, cmdstr);
if (!cb->longfmt) { if (!cb->longfmt) {
(void) printf("\n"); (void) printf("\n");
continue; continue;
} }
(void) printf(" ["); (void) printf(" [");
if (nvlist_lookup_uint64(records[i], if (nvlist_exists(rec, ZPOOL_HIST_WHO)) {
ZPOOL_HIST_WHO, &who) == 0) { uid_t who = fnvlist_lookup_uint64(rec, ZPOOL_HIST_WHO);
pwd = getpwuid((uid_t)who); struct passwd *pwd = getpwuid(who);
if (pwd) (void) printf("user %d ", (int)who);
(void) printf("user %s on", if (pwd != NULL)
pwd->pw_name); (void) printf("(%s) ", pwd->pw_name);
else
(void) printf("user %d on",
(int)who);
} else {
(void) printf(gettext("no info]\n"));
continue;
} }
if (nvlist_lookup_string(records[i], if (nvlist_exists(rec, ZPOOL_HIST_HOST)) {
ZPOOL_HIST_HOST, &hostname) == 0) { (void) printf("on %s",
(void) printf(" %s", hostname); fnvlist_lookup_string(rec, ZPOOL_HIST_HOST));
} }
if (nvlist_lookup_string(records[i], if (nvlist_exists(rec, ZPOOL_HIST_ZONE)) {
ZPOOL_HIST_ZONE, &zonename) == 0) { (void) printf(":%s",
(void) printf(":%s", zonename); fnvlist_lookup_string(rec, ZPOOL_HIST_ZONE));
} }
(void) printf("]"); (void) printf("]");
(void) printf("\n"); (void) printf("\n");
} }
@ -4602,8 +4633,6 @@ get_history_one(zpool_handle_t *zhp, void *data)
* *
* Displays the history of commands that modified pools. * Displays the history of commands that modified pools.
*/ */
int int
zpool_do_history(int argc, char **argv) zpool_do_history(int argc, char **argv)
{ {
@ -4616,10 +4645,10 @@ zpool_do_history(int argc, char **argv)
while ((c = getopt(argc, argv, "li")) != -1) { while ((c = getopt(argc, argv, "li")) != -1) {
switch (c) { switch (c) {
case 'l': case 'l':
cbdata.longfmt = 1; cbdata.longfmt = B_TRUE;
break; break;
case 'i': case 'i':
cbdata.internal = 1; cbdata.internal = B_TRUE;
break; break;
case '?': case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"), (void) fprintf(stderr, gettext("invalid option '%c'\n"),
@ -4844,8 +4873,7 @@ main(int argc, char **argv)
if (strcmp(cmdname, "-?") == 0) if (strcmp(cmdname, "-?") == 0)
usage(B_TRUE); usage(B_TRUE);
zpool_set_history_str("zpool", argc, argv, history_str); zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
verify(zpool_stage_history(g_zfs, history_str) == 0);
/* /*
* Run the appropriate command. * Run the appropriate command.
@ -4872,6 +4900,9 @@ main(int argc, char **argv)
usage(B_FALSE); usage(B_FALSE);
} }
if (ret == 0 && log_history)
(void) zpool_log_history(g_zfs, history_str);
libzfs_fini(g_zfs); libzfs_fini(g_zfs);
/* /*

View File

@ -2252,7 +2252,7 @@ ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id)
*/ */
nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1); nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1);
VERIFY3U(ENOENT, ==, VERIFY3U(ENOENT, ==,
spa_create("ztest_bad_file", nvroot, NULL, NULL, NULL)); spa_create("ztest_bad_file", nvroot, NULL, NULL));
nvlist_free(nvroot); nvlist_free(nvroot);
/* /*
@ -2260,7 +2260,7 @@ ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id)
*/ */
nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 2, 1); nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 2, 1);
VERIFY3U(ENOENT, ==, VERIFY3U(ENOENT, ==,
spa_create("ztest_bad_mirror", nvroot, NULL, NULL, NULL)); spa_create("ztest_bad_mirror", nvroot, NULL, NULL));
nvlist_free(nvroot); nvlist_free(nvroot);
/* /*
@ -2269,7 +2269,7 @@ ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id)
*/ */
(void) rw_rdlock(&ztest_name_lock); (void) rw_rdlock(&ztest_name_lock);
nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1); nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1);
VERIFY3U(EEXIST, ==, spa_create(zo->zo_pool, nvroot, NULL, NULL, NULL)); VERIFY3U(EEXIST, ==, spa_create(zo->zo_pool, nvroot, NULL, NULL));
nvlist_free(nvroot); nvlist_free(nvroot);
VERIFY3U(0, ==, spa_open(zo->zo_pool, &spa, FTAG)); VERIFY3U(0, ==, spa_open(zo->zo_pool, &spa, FTAG));
VERIFY3U(EBUSY, ==, spa_destroy(zo->zo_pool)); VERIFY3U(EBUSY, ==, spa_destroy(zo->zo_pool));
@ -3056,8 +3056,7 @@ ztest_snapshot_create(char *osname, uint64_t id)
(void) snprintf(snapname, MAXNAMELEN, "%s@%llu", osname, (void) snprintf(snapname, MAXNAMELEN, "%s@%llu", osname,
(u_longlong_t)id); (u_longlong_t)id);
error = dmu_objset_snapshot(osname, strchr(snapname, '@') + 1, error = dmu_objset_snapshot_one(osname, strchr(snapname, '@') + 1);
NULL, NULL, B_FALSE, B_FALSE, -1);
if (error == ENOSPC) { if (error == ENOSPC) {
ztest_record_enospc(FTAG); ztest_record_enospc(FTAG);
return (B_FALSE); return (B_FALSE);
@ -3257,8 +3256,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
(void) snprintf(clone2name, MAXNAMELEN, "%s/c2_%llu", osname, id); (void) snprintf(clone2name, MAXNAMELEN, "%s/c2_%llu", osname, id);
(void) snprintf(snap3name, MAXNAMELEN, "%s@s3_%llu", clone1name, id); (void) snprintf(snap3name, MAXNAMELEN, "%s@s3_%llu", clone1name, id);
error = dmu_objset_snapshot(osname, strchr(snap1name, '@')+1, error = dmu_objset_snapshot_one(osname, strchr(snap1name, '@') + 1);
NULL, NULL, B_FALSE, B_FALSE, -1);
if (error && error != EEXIST) { if (error && error != EEXIST) {
if (error == ENOSPC) { if (error == ENOSPC) {
ztest_record_enospc(FTAG); ztest_record_enospc(FTAG);
@ -3281,8 +3279,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_objset_create(%s) = %d", clone1name, error); fatal(0, "dmu_objset_create(%s) = %d", clone1name, error);
} }
error = dmu_objset_snapshot(clone1name, strchr(snap2name, '@')+1, error = dmu_objset_snapshot_one(clone1name, strchr(snap2name, '@') + 1);
NULL, NULL, B_FALSE, B_FALSE, -1);
if (error && error != EEXIST) { if (error && error != EEXIST) {
if (error == ENOSPC) { if (error == ENOSPC) {
ztest_record_enospc(FTAG); ztest_record_enospc(FTAG);
@ -3291,8 +3288,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_open_snapshot(%s) = %d", snap2name, error); fatal(0, "dmu_open_snapshot(%s) = %d", snap2name, error);
} }
error = dmu_objset_snapshot(clone1name, strchr(snap3name, '@')+1, error = dmu_objset_snapshot_one(clone1name, strchr(snap3name, '@') + 1);
NULL, NULL, B_FALSE, B_FALSE, -1);
if (error && error != EEXIST) { if (error && error != EEXIST) {
if (error == ENOSPC) { if (error == ENOSPC) {
ztest_record_enospc(FTAG); ztest_record_enospc(FTAG);
@ -4480,8 +4476,7 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
* Create snapshot, clone it, mark snap for deferred destroy, * Create snapshot, clone it, mark snap for deferred destroy,
* destroy clone, verify snap was also destroyed. * destroy clone, verify snap was also destroyed.
*/ */
error = dmu_objset_snapshot(osname, snapname, NULL, NULL, FALSE, error = dmu_objset_snapshot_one(osname, snapname);
FALSE, -1);
if (error) { if (error) {
if (error == ENOSPC) { if (error == ENOSPC) {
ztest_record_enospc("dmu_objset_snapshot"); ztest_record_enospc("dmu_objset_snapshot");
@ -4523,8 +4518,7 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
* destroy a held snapshot, mark for deferred destroy, * destroy a held snapshot, mark for deferred destroy,
* release hold, verify snapshot was destroyed. * release hold, verify snapshot was destroyed.
*/ */
error = dmu_objset_snapshot(osname, snapname, NULL, NULL, FALSE, error = dmu_objset_snapshot_one(osname, snapname);
FALSE, -1);
if (error) { if (error) {
if (error == ENOSPC) { if (error == ENOSPC) {
ztest_record_enospc("dmu_objset_snapshot"); ztest_record_enospc("dmu_objset_snapshot");
@ -5612,8 +5606,7 @@ ztest_init(ztest_shared_t *zs)
spa_feature_table[i].fi_uname); spa_feature_table[i].fi_uname);
VERIFY3U(0, ==, nvlist_add_uint64(props, buf, 0)); VERIFY3U(0, ==, nvlist_add_uint64(props, buf, 0));
} }
VERIFY3U(0, ==, spa_create(ztest_opts.zo_pool, nvroot, props, VERIFY3U(0, ==, spa_create(ztest_opts.zo_pool, nvroot, props, NULL));
NULL, NULL));
nvlist_free(nvroot); nvlist_free(nvroot);
VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG)); VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));

View File

@ -54,7 +54,8 @@ extern "C" {
/* /*
* libzfs errors * libzfs errors
*/ */
enum { typedef enum zfs_error {
EZFS_SUCCESS = 0, /* no error -- success */
EZFS_NOMEM = 2000, /* out of memory */ EZFS_NOMEM = 2000, /* out of memory */
EZFS_BADPROP, /* invalid property value */ EZFS_BADPROP, /* invalid property value */
EZFS_PROPREADONLY, /* cannot set readonly property */ EZFS_PROPREADONLY, /* cannot set readonly property */
@ -126,7 +127,7 @@ enum {
EZFS_DIFFDATA, /* bad zfs diff data */ EZFS_DIFFDATA, /* bad zfs diff data */
EZFS_POOLREADONLY, /* pool is in read-only mode */ EZFS_POOLREADONLY, /* pool is in read-only mode */
EZFS_UNKNOWN EZFS_UNKNOWN
}; } zfs_error_t;
/* /*
* The following data structures are all part * The following data structures are all part
@ -182,6 +183,9 @@ extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *);
extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t); extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t);
extern void zfs_save_arguments(int argc, char **, char *, int);
extern int zpool_log_history(libzfs_handle_t *, const char *);
extern int libzfs_errno(libzfs_handle_t *); extern int libzfs_errno(libzfs_handle_t *);
extern const char *libzfs_error_action(libzfs_handle_t *); extern const char *libzfs_error_action(libzfs_handle_t *);
extern const char *libzfs_error_description(libzfs_handle_t *); extern const char *libzfs_error_description(libzfs_handle_t *);
@ -216,7 +220,7 @@ extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
*/ */
extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *, extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *,
nvlist_t *, nvlist_t *); nvlist_t *, nvlist_t *);
extern int zpool_destroy(zpool_handle_t *); extern int zpool_destroy(zpool_handle_t *, const char *);
extern int zpool_add(zpool_handle_t *, nvlist_t *); extern int zpool_add(zpool_handle_t *, nvlist_t *);
typedef struct splitflags { typedef struct splitflags {
@ -338,8 +342,8 @@ extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
/* /*
* Import and export functions * Import and export functions
*/ */
extern int zpool_export(zpool_handle_t *, boolean_t); extern int zpool_export(zpool_handle_t *, boolean_t, const char *);
extern int zpool_export_force(zpool_handle_t *); extern int zpool_export_force(zpool_handle_t *, const char *);
extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *, extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
char *altroot); char *altroot);
extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *, extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
@ -373,7 +377,7 @@ extern nvlist_t *zpool_find_import_cached(libzfs_handle_t *, const char *,
*/ */
struct zfs_cmd; struct zfs_cmd;
extern const char *zfs_history_event_names[LOG_END]; extern const char *zfs_history_event_names[];
extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *, extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *,
boolean_t verbose); boolean_t verbose);
@ -381,9 +385,6 @@ extern int zpool_upgrade(zpool_handle_t *, uint64_t);
extern int zpool_get_history(zpool_handle_t *, nvlist_t **); extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
extern int zpool_history_unpack(char *, uint64_t, uint64_t *, extern int zpool_history_unpack(char *, uint64_t, uint64_t *,
nvlist_t ***, uint_t *); nvlist_t ***, uint_t *);
extern void zpool_set_history_str(const char *subcommand, int argc,
char **argv, char *history_str);
extern int zpool_stage_history(libzfs_handle_t *, const char *);
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *, extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
size_t len); size_t len);
extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *); extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *);
@ -436,8 +437,6 @@ extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
char *propbuf, int proplen, boolean_t literal); char *propbuf, int proplen, boolean_t literal);
extern int zfs_prop_get_feature(zfs_handle_t *zhp, const char *propname, extern int zfs_prop_get_feature(zfs_handle_t *zhp, const char *propname,
char *buf, size_t len); char *buf, size_t len);
extern int zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
uint64_t *usedp);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t); extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t); extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
extern const char *zfs_prop_values(zfs_prop_t); extern const char *zfs_prop_values(zfs_prop_t);
@ -553,6 +552,8 @@ extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
extern int zfs_destroy_snaps_nvl(zfs_handle_t *, nvlist_t *, boolean_t); extern int zfs_destroy_snaps_nvl(zfs_handle_t *, nvlist_t *, boolean_t);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *); extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *); extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
extern int zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps,
nvlist_t *props);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t); extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
extern int zfs_rename(zfs_handle_t *, const char *, boolean_t, boolean_t); extern int zfs_rename(zfs_handle_t *, const char *, boolean_t, boolean_t);

View File

@ -336,6 +336,48 @@ zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
return (0); return (0);
} }
/*
* If the __ZFS_POOL_RESTRICT environment variable is set we only iterate over
* pools it lists.
*
* This is an undocumented feature for use during testing only.
*
* This function returns B_TRUE if the pool should be skipped
* during iteration.
*/
static boolean_t
check_restricted(const char *poolname)
{
static boolean_t initialized = B_FALSE;
static char *restricted = NULL;
const char *cur, *end;
int len, namelen;
if (!initialized) {
initialized = B_TRUE;
restricted = getenv("__ZFS_POOL_RESTRICT");
}
if (NULL == restricted)
return (B_FALSE);
cur = restricted;
namelen = strlen(poolname);
do {
end = strchr(cur, ' ');
len = (NULL == end) ? strlen(cur) : (end - cur);
if (len == namelen && 0 == strncmp(cur, poolname, len)) {
return (B_FALSE);
}
cur += (len + 1);
} while (NULL != end);
return (B_TRUE);
}
/* /*
* Iterate over all pools in the system. * Iterate over all pools in the system.
*/ */
@ -359,6 +401,9 @@ zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL; for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) { cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
if (check_restricted(cn->cn_name))
continue;
if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0) { if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0) {
hdl->libzfs_pool_iter--; hdl->libzfs_pool_iter--;
return (-1); return (-1);
@ -394,6 +439,9 @@ zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)
for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL; for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) { cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
if (check_restricted(cn->cn_name))
continue;
if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL) if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)
continue; continue;

View File

@ -1407,8 +1407,7 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
libzfs_handle_t *hdl = zhp->zfs_hdl; libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *nvl = NULL, *realprops; nvlist_t *nvl = NULL, *realprops;
zfs_prop_t prop; zfs_prop_t prop;
boolean_t do_prefix; boolean_t do_prefix = B_TRUE;
uint64_t idx;
int added_resv; int added_resv;
(void) snprintf(errbuf, sizeof (errbuf), (void) snprintf(errbuf, sizeof (errbuf),
@ -1447,12 +1446,17 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
} }
/* /*
* If the dataset's canmount property is being set to noauto, * We don't want to unmount & remount the dataset when changing
* then we want to prevent unmounting & remounting it. * its canmount property to 'on' or 'noauto'. We only use
* the changelist logic to unmount when setting canmount=off.
*/ */
do_prefix = !((prop == ZFS_PROP_CANMOUNT) && if (prop == ZFS_PROP_CANMOUNT) {
(zprop_string_to_index(prop, propval, &idx, uint64_t idx;
ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO)); int err = zprop_string_to_index(prop, propval, &idx,
ZFS_TYPE_DATASET);
if (err == 0 && idx != ZFS_CANMOUNT_OFF)
do_prefix = B_FALSE;
}
if (do_prefix && (ret = changelist_prefix(cl)) != 0) if (do_prefix && (ret = changelist_prefix(cl)) != 0)
goto error; goto error;
@ -2641,25 +2645,6 @@ zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
return (0); return (0);
} }
int
zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
uint64_t *usedp)
{
int err;
zfs_cmd_t zc = { 0 };
(void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
if (err)
return (err);
*usedp = zc.zc_cookie;
return (0);
}
/* /*
* Returns the name of the given zfs handle. * Returns the name of the given zfs handle.
*/ */
@ -2860,7 +2845,6 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
*/ */
for (cp = target + prefixlen + 1; for (cp = target + prefixlen + 1;
cp = strchr(cp, '/'); *cp = '/', cp++) { cp = strchr(cp, '/'); *cp = '/', cp++) {
char *logstr;
*cp = '\0'; *cp = '\0';
@ -2871,16 +2855,12 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
continue; continue;
} }
logstr = hdl->libzfs_log_str;
hdl->libzfs_log_str = NULL;
if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM, if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
NULL) != 0) { NULL) != 0) {
hdl->libzfs_log_str = logstr;
opname = dgettext(TEXT_DOMAIN, "create"); opname = dgettext(TEXT_DOMAIN, "create");
goto ancestorerr; goto ancestorerr;
} }
hdl->libzfs_log_str = logstr;
h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM); h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
if (h == NULL) { if (h == NULL) {
opname = dgettext(TEXT_DOMAIN, "open"); opname = dgettext(TEXT_DOMAIN, "open");
@ -2938,12 +2918,12 @@ int
zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
nvlist_t *props) nvlist_t *props)
{ {
zfs_cmd_t zc = { 0 };
int ret; int ret;
uint64_t size = 0; uint64_t size = 0;
uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
char errbuf[1024]; char errbuf[1024];
uint64_t zoned; uint64_t zoned;
dmu_objset_type_t ost;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), path); "cannot create '%s'"), path);
@ -2963,17 +2943,16 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
* will return ENOENT, not EEXIST. To prevent this from happening, we * will return ENOENT, not EEXIST. To prevent this from happening, we
* first try to see if the dataset exists. * first try to see if the dataset exists.
*/ */
(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"dataset already exists")); "dataset already exists"));
return (zfs_error(hdl, EZFS_EXISTS, errbuf)); return (zfs_error(hdl, EZFS_EXISTS, errbuf));
} }
if (type == ZFS_TYPE_VOLUME) if (type == ZFS_TYPE_VOLUME)
zc.zc_objset_type = DMU_OST_ZVOL; ost = DMU_OST_ZVOL;
else else
zc.zc_objset_type = DMU_OST_ZFS; ost = DMU_OST_ZFS;
if (props && (props = zfs_valid_proplist(hdl, type, props, if (props && (props = zfs_valid_proplist(hdl, type, props,
zoned, NULL, errbuf)) == 0) zoned, NULL, errbuf)) == 0)
@ -3025,14 +3004,9 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
} }
} }
if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
return (-1);
nvlist_free(props);
/* create the dataset */ /* create the dataset */
ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); ret = lzc_create(path, ost, props);
nvlist_free(props);
zcmd_free_nvlists(&zc);
/* check for failure */ /* check for failure */
if (ret != 0) { if (ret != 0) {
@ -3084,7 +3058,8 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
/* /*
* Destroys the given dataset. The caller must make sure that the filesystem * Destroys the given dataset. The caller must make sure that the filesystem
* isn't mounted, and that there are no active dependents. * isn't mounted, and that there are no active dependents. If the file system
* does not exist this function does nothing.
*/ */
int int
zfs_destroy(zfs_handle_t *zhp, boolean_t defer) zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
@ -3100,7 +3075,8 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
} }
zc.zc_defer_destroy = defer; zc.zc_defer_destroy = defer;
if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) { if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 &&
errno != ENOENT) {
return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
zhp->zfs_name)); zhp->zfs_name));
@ -3170,33 +3146,35 @@ int
zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer) zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
{ {
int ret; int ret;
zfs_cmd_t zc = { 0 }; nvlist_t *errlist;
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); ret = lzc_destroy_snaps(snaps, defer, &errlist);
if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
return (-1);
zc.zc_defer_destroy = defer;
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
if (ret != 0) { if (ret != 0) {
char errbuf[1024]; for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
nvpair_name(pair));
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, switch (fnvpair_value_int32(pair)) {
"cannot destroy snapshots in %s"), zc.zc_name); case EEXIST:
zfs_error_aux(zhp->zfs_hdl,
switch (errno) { dgettext(TEXT_DOMAIN,
case EEXIST: "snapshot is cloned"));
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, ret = zfs_error(zhp->zfs_hdl, EZFS_EXISTS,
"snapshot is cloned")); errbuf);
return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf)); break;
default:
default: ret = zfs_standard_error(zhp->zfs_hdl, errno,
return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
errbuf)); break;
}
} }
} }
return (0); return (ret);
} }
/* /*
@ -3205,12 +3183,10 @@ zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
int int
zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
{ {
zfs_cmd_t zc = { 0 };
char parent[ZFS_MAXNAMELEN]; char parent[ZFS_MAXNAMELEN];
int ret; int ret;
char errbuf[1024]; char errbuf[1024];
libzfs_handle_t *hdl = zhp->zfs_hdl; libzfs_handle_t *hdl = zhp->zfs_hdl;
zfs_type_t type;
uint64_t zoned; uint64_t zoned;
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
@ -3229,32 +3205,21 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
(void) parent_name(target, parent, sizeof (parent)); (void) parent_name(target, parent, sizeof (parent));
/* do the clone */ /* do the clone */
if (ZFS_IS_VOLUME(zhp)) {
zc.zc_objset_type = DMU_OST_ZVOL;
type = ZFS_TYPE_VOLUME;
} else {
zc.zc_objset_type = DMU_OST_ZFS;
type = ZFS_TYPE_FILESYSTEM;
}
if (props) { if (props) {
zfs_type_t type;
if (ZFS_IS_VOLUME(zhp)) {
type = ZFS_TYPE_VOLUME;
} else {
type = ZFS_TYPE_FILESYSTEM;
}
if ((props = zfs_valid_proplist(hdl, type, props, zoned, if ((props = zfs_valid_proplist(hdl, type, props, zoned,
zhp, errbuf)) == NULL) zhp, errbuf)) == NULL)
return (-1); return (-1);
if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
nvlist_free(props);
return (-1);
}
nvlist_free(props);
} }
(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name)); ret = lzc_clone(target, zhp->zfs_name, props);
(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value)); nvlist_free(props);
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
zcmd_free_nvlists(&zc);
if (ret != 0) { if (ret != 0) {
switch (errno) { switch (errno) {
@ -3339,74 +3304,134 @@ zfs_promote(zfs_handle_t *zhp)
return (ret); return (ret);
} }
typedef struct snapdata {
nvlist_t *sd_nvl;
const char *sd_snapname;
} snapdata_t;
static int
zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
{
snapdata_t *sd = arg;
char name[ZFS_MAXNAMELEN];
int rv = 0;
(void) snprintf(name, sizeof (name),
"%s@%s", zfs_get_name(zhp), sd->sd_snapname);
fnvlist_add_boolean(sd->sd_nvl, name);
rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
zfs_close(zhp);
return (rv);
}
/* /*
* Takes a snapshot of the given dataset. * Creates snapshots. The keys in the snaps nvlist are the snapshots to be
* created.
*/ */
int
zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
{
int ret;
char errbuf[1024];
nvpair_t *elem;
nvlist_t *errors;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create snapshots "));
elem = NULL;
while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
const char *snapname = nvpair_name(elem);
/* validate the target name */
if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
B_TRUE)) {
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN,
"cannot create snapshot '%s'"), snapname);
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
}
}
if (props != NULL &&
(props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
props, B_FALSE, NULL, errbuf)) == NULL) {
return (-1);
}
ret = lzc_snapshot(snaps, props, &errors);
if (ret != 0) {
boolean_t printed = B_FALSE;
for (elem = nvlist_next_nvpair(errors, NULL);
elem != NULL;
elem = nvlist_next_nvpair(errors, elem)) {
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN,
"cannot create snapshot '%s'"), nvpair_name(elem));
(void) zfs_standard_error(hdl,
fnvpair_value_int32(elem), errbuf);
printed = B_TRUE;
}
if (!printed) {
switch (ret) {
case EXDEV:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"multiple snapshots of same "
"fs not allowed"));
(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
break;
default:
(void) zfs_standard_error(hdl, ret, errbuf);
}
}
}
nvlist_free(props);
nvlist_free(errors);
return (ret);
}
int int
zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
nvlist_t *props) nvlist_t *props)
{ {
const char *delim;
char parent[ZFS_MAXNAMELEN];
zfs_handle_t *zhp;
zfs_cmd_t zc = { 0 };
int ret; int ret;
snapdata_t sd = { 0 };
char fsname[ZFS_MAXNAMELEN];
char *cp;
zfs_handle_t *zhp;
char errbuf[1024]; char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot snapshot '%s'"), path); "cannot snapshot %s"), path);
/* validate the target name */
if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE)) if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
if (props) { (void) strlcpy(fsname, path, sizeof (fsname));
if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, cp = strchr(fsname, '@');
props, B_FALSE, NULL, errbuf)) == NULL) *cp = '\0';
return (-1); sd.sd_snapname = cp + 1;
if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) { if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
nvlist_free(props);
return (-1);
}
nvlist_free(props);
}
/* make sure the parent exists and is of the appropriate type */
delim = strchr(path, '@');
(void) strncpy(parent, path, delim - path);
parent[delim - path] = '\0';
if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
ZFS_TYPE_VOLUME)) == NULL) { ZFS_TYPE_VOLUME)) == NULL) {
zcmd_free_nvlists(&zc);
return (-1); return (-1);
} }
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value)); if (recursive) {
if (ZFS_IS_VOLUME(zhp)) (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
zc.zc_objset_type = DMU_OST_ZVOL; } else {
else fnvlist_add_boolean(sd.sd_nvl, path);
zc.zc_objset_type = DMU_OST_ZFS;
zc.zc_cookie = recursive;
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
zcmd_free_nvlists(&zc);
/*
* if it was recursive, the one that actually failed will be in
* zc.zc_name.
*/
if (ret != 0) {
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
(void) zfs_standard_error(hdl, errno, errbuf);
} }
ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
nvlist_free(sd.sd_nvl);
zfs_close(zhp); zfs_close(zhp);
return (ret); return (ret);
} }
@ -3434,17 +3459,13 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
cbp->cb_create) { cbp->cb_create) {
char *logstr;
cbp->cb_dependent = B_TRUE; cbp->cb_dependent = B_TRUE;
cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE, cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
rollback_destroy, cbp); rollback_destroy, cbp);
cbp->cb_dependent = B_FALSE; cbp->cb_dependent = B_FALSE;
logstr = zhp->zfs_hdl->libzfs_log_str;
zhp->zfs_hdl->libzfs_log_str = NULL;
cbp->cb_error |= zfs_destroy(zhp, B_FALSE); cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
zhp->zfs_hdl->libzfs_log_str = logstr;
} }
} else { } else {
/* We must destroy this clone; first unmount it */ /* We must destroy this clone; first unmount it */

View File

@ -21,11 +21,11 @@
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved.
*/ */
#ifndef _LIBFS_IMPL_H #ifndef _LIBZFS_IMPL_H
#define _LIBFS_IMPL_H #define _LIBZFS_IMPL_H
#include <sys/dmu.h> #include <sys/dmu.h>
#include <sys/fs/zfs.h> #include <sys/fs/zfs.h>
@ -36,6 +36,7 @@
#include <libuutil.h> #include <libuutil.h>
#include <libzfs.h> #include <libzfs.h>
#include <libshare.h> #include <libshare.h>
#include <libzfs_core.h>
#include <fm/libtopo.h> #include <fm/libtopo.h>
@ -67,7 +68,6 @@ struct libzfs_handle {
int libzfs_desc_active; int libzfs_desc_active;
char libzfs_action[1024]; char libzfs_action[1024];
char libzfs_desc[1024]; char libzfs_desc[1024];
char *libzfs_log_str;
int libzfs_printerr; int libzfs_printerr;
int libzfs_storeerr; /* stuff error messages into buffer */ int libzfs_storeerr; /* stuff error messages into buffer */
void *libzfs_sharehdl; /* libshare handle */ void *libzfs_sharehdl; /* libshare handle */
@ -213,4 +213,4 @@ extern void libzfs_fru_clear(libzfs_handle_t *, boolean_t);
} }
#endif #endif
#endif /* _LIBFS_IMPL_H */ #endif /* _LIBZFS_IMPL_H */

View File

@ -22,7 +22,7 @@
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved.
*/ */
#include <stdio.h> #include <stdio.h>
@ -301,12 +301,11 @@ int
zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig, zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
zfs_iter_f func, void *arg) zfs_iter_f func, void *arg)
{ {
char buf[ZFS_MAXNAMELEN]; char *buf, *comma_separated, *cp;
char *comma_separated, *cp;
int err = 0; int err = 0;
int ret = 0; int ret = 0;
(void) strlcpy(buf, spec_orig, sizeof (buf)); buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig);
cp = buf; cp = buf;
while ((comma_separated = strsep(&cp, ",")) != NULL) { while ((comma_separated = strsep(&cp, ",")) != NULL) {
@ -364,6 +363,7 @@ zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
} }
} }
free(buf);
return (ret); return (ret);
} }

View File

@ -34,6 +34,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <strings.h> #include <strings.h>
#include <unistd.h> #include <unistd.h>
#include <libgen.h>
#include <sys/efi_partition.h> #include <sys/efi_partition.h>
#include <sys/vtoc.h> #include <sys/vtoc.h>
#include <sys/zfs_ioctl.h> #include <sys/zfs_ioctl.h>
@ -1205,7 +1206,7 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
* datasets left in the pool. * datasets left in the pool.
*/ */
int int
zpool_destroy(zpool_handle_t *zhp) zpool_destroy(zpool_handle_t *zhp, const char *log_str)
{ {
zfs_cmd_t zc = { 0 }; zfs_cmd_t zc = { 0 };
zfs_handle_t *zfp = NULL; zfs_handle_t *zfp = NULL;
@ -1217,6 +1218,7 @@ zpool_destroy(zpool_handle_t *zhp)
return (-1); return (-1);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_history = (uint64_t)(uintptr_t)log_str;
if (zfs_ioctl(hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) { if (zfs_ioctl(hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
@ -1371,8 +1373,9 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
* Exports the pool from the system. The caller must ensure that there are no * Exports the pool from the system. The caller must ensure that there are no
* mounted datasets in the pool. * mounted datasets in the pool.
*/ */
int static int
zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce) zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce,
const char *log_str)
{ {
zfs_cmd_t zc = { 0 }; zfs_cmd_t zc = { 0 };
char msg[1024]; char msg[1024];
@ -1383,6 +1386,7 @@ zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_cookie = force; zc.zc_cookie = force;
zc.zc_guid = hardforce; zc.zc_guid = hardforce;
zc.zc_history = (uint64_t)(uintptr_t)log_str;
if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) { if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) {
switch (errno) { switch (errno) {
@ -1404,15 +1408,15 @@ zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce)
} }
int int
zpool_export(zpool_handle_t *zhp, boolean_t force) zpool_export(zpool_handle_t *zhp, boolean_t force, const char *log_str)
{ {
return (zpool_export_common(zhp, force, B_FALSE)); return (zpool_export_common(zhp, force, B_FALSE, log_str));
} }
int int
zpool_export_force(zpool_handle_t *zhp) zpool_export_force(zpool_handle_t *zhp, const char *log_str)
{ {
return (zpool_export_common(zhp, B_TRUE, B_TRUE)); return (zpool_export_common(zhp, B_TRUE, B_TRUE, log_str));
} }
static void static void
@ -3574,40 +3578,30 @@ zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version)
} }
void void
zpool_set_history_str(const char *subcommand, int argc, char **argv, zfs_save_arguments(int argc, char **argv, char *string, int len)
char *history_str)
{ {
int i; (void) strlcpy(string, basename(argv[0]), len);
for (int i = 1; i < argc; i++) {
(void) strlcpy(history_str, subcommand, HIS_MAX_RECORD_LEN); (void) strlcat(string, " ", len);
for (i = 1; i < argc; i++) { (void) strlcat(string, argv[i], len);
if (strlen(history_str) + 1 + strlen(argv[i]) >
HIS_MAX_RECORD_LEN)
break;
(void) strlcat(history_str, " ", HIS_MAX_RECORD_LEN);
(void) strlcat(history_str, argv[i], HIS_MAX_RECORD_LEN);
} }
} }
/*
* Stage command history for logging.
*/
int int
zpool_stage_history(libzfs_handle_t *hdl, const char *history_str) zpool_log_history(libzfs_handle_t *hdl, const char *message)
{ {
if (history_str == NULL) zfs_cmd_t zc = { 0 };
return (EINVAL); nvlist_t *args;
int err;
if (strlen(history_str) > HIS_MAX_RECORD_LEN) args = fnvlist_alloc();
return (EINVAL); fnvlist_add_string(args, "message", message);
err = zcmd_write_src_nvlist(hdl, &zc, args);
if (hdl->libzfs_log_str != NULL) if (err == 0)
free(hdl->libzfs_log_str); err = ioctl(hdl->libzfs_fd, ZFS_IOC_LOG_HISTORY, &zc);
nvlist_free(args);
if ((hdl->libzfs_log_str = strdup(history_str)) == NULL) zcmd_free_nvlists(&zc);
return (no_memory(hdl)); return (err);
return (0);
} }
/* /*

View File

@ -21,7 +21,7 @@
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/ */
@ -1381,7 +1381,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
avl_tree_t *fsavl = NULL; avl_tree_t *fsavl = NULL;
static uint64_t holdseq; static uint64_t holdseq;
int spa_version; int spa_version;
boolean_t holdsnaps = B_FALSE;
pthread_t tid; pthread_t tid;
int pipefd[2]; int pipefd[2];
dedup_arg_t dda = { 0 }; dedup_arg_t dda = { 0 };
@ -1404,11 +1403,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
} }
} }
if (!flags->dryrun && zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS &&
(flags->doall || flags->replicate))
holdsnaps = B_TRUE;
if (flags->dedup && !flags->dryrun) { if (flags->dedup && !flags->dryrun) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP | featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS); DMU_BACKUP_FEATURE_DEDUPPROPS);
@ -1530,7 +1524,18 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.filter_cb_arg = cb_arg; sdd.filter_cb_arg = cb_arg;
if (debugnvp) if (debugnvp)
sdd.debugnv = *debugnvp; sdd.debugnv = *debugnvp;
if (holdsnaps || flags->progress) {
/*
* Some flags require that we place user holds on the datasets that are
* being sent so they don't get destroyed during the send. We can skip
* this step if the pool is imported read-only since the datasets cannot
* be destroyed.
*/
if (!flags->dryrun && !zpool_get_prop_int(zfs_get_pool_handle(zhp),
ZPOOL_PROP_READONLY, NULL) &&
zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS &&
(flags->doall || flags->replicate)) {
++holdseq; ++holdseq;
(void) snprintf(sdd.holdtag, sizeof (sdd.holdtag), (void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
".send-%d-%llu", getpid(), (u_longlong_t)holdseq); ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);

View File

@ -43,6 +43,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <libzfs.h> #include <libzfs.h>
#include <libzfs_core.h>
#include "libzfs_impl.h" #include "libzfs_impl.h"
#include "zfs_prop.h" #include "zfs_prop.h"
@ -630,6 +631,14 @@ libzfs_init(void)
hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r"); hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r");
if (libzfs_core_init() != 0) {
(void) close(hdl->libzfs_fd);
(void) fclose(hdl->libzfs_mnttab);
(void) fclose(hdl->libzfs_sharetab);
free(hdl);
return (NULL);
}
zfs_prop_init(); zfs_prop_init();
zpool_prop_init(); zpool_prop_init();
zpool_feature_init(); zpool_feature_init();
@ -647,12 +656,11 @@ libzfs_fini(libzfs_handle_t *hdl)
if (hdl->libzfs_sharetab) if (hdl->libzfs_sharetab)
(void) fclose(hdl->libzfs_sharetab); (void) fclose(hdl->libzfs_sharetab);
zfs_uninit_libshare(hdl); zfs_uninit_libshare(hdl);
if (hdl->libzfs_log_str)
(void) free(hdl->libzfs_log_str);
zpool_free_handles(hdl); zpool_free_handles(hdl);
libzfs_fru_clear(hdl, B_TRUE); libzfs_fru_clear(hdl, B_TRUE);
namespace_clear(hdl); namespace_clear(hdl);
libzfs_mnttab_fini(hdl); libzfs_mnttab_fini(hdl);
libzfs_core_fini();
free(hdl); free(hdl);
} }
@ -814,17 +822,7 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
int int
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc) zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
{ {
int error; return (ioctl(hdl->libzfs_fd, request, zc));
zc->zc_history = (uint64_t)(uintptr_t)hdl->libzfs_log_str;
error = ioctl(hdl->libzfs_fd, request, zc);
if (hdl->libzfs_log_str) {
free(hdl->libzfs_log_str);
hdl->libzfs_log_str = NULL;
}
zc->zc_history = 0;
return (error);
} }
/* /*

View File

@ -0,0 +1,477 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
/*
* LibZFS_Core (lzc) is intended to replace most functionality in libzfs.
* It has the following characteristics:
*
* - Thread Safe. libzfs_core is accessible concurrently from multiple
* threads. This is accomplished primarily by avoiding global data
* (e.g. caching). Since it's thread-safe, there is no reason for a
* process to have multiple libzfs "instances". Therefore, we store
* our few pieces of data (e.g. the file descriptor) in global
* variables. The fd is reference-counted so that the libzfs_core
* library can be "initialized" multiple times (e.g. by different
* consumers within the same process).
*
* - Committed Interface. The libzfs_core interface will be committed,
* therefore consumers can compile against it and be confident that
* their code will continue to work on future releases of this code.
* Currently, the interface is Evolving (not Committed), but we intend
* to commit to it once it is more complete and we determine that it
* meets the needs of all consumers.
*
* - Programatic Error Handling. libzfs_core communicates errors with
* defined error numbers, and doesn't print anything to stdout/stderr.
*
* - Thin Layer. libzfs_core is a thin layer, marshaling arguments
* to/from the kernel ioctls. There is generally a 1:1 correspondence
* between libzfs_core functions and ioctls to /dev/zfs.
*
* - Clear Atomicity. Because libzfs_core functions are generally 1:1
* with kernel ioctls, and kernel ioctls are general atomic, each
* libzfs_core function is atomic. For example, creating multiple
* snapshots with a single call to lzc_snapshot() is atomic -- it
* can't fail with only some of the requested snapshots created, even
* in the event of power loss or system crash.
*
* - Continued libzfs Support. Some higher-level operations (e.g.
* support for "zfs send -R") are too complicated to fit the scope of
* libzfs_core. This functionality will continue to live in libzfs.
* Where appropriate, libzfs will use the underlying atomic operations
* of libzfs_core. For example, libzfs may implement "zfs send -R |
* zfs receive" by using individual "send one snapshot", rename,
* destroy, and "receive one snapshot" operations in libzfs_core.
* /sbin/zfs and /zbin/zpool will link with both libzfs and
* libzfs_core. Other consumers should aim to use only libzfs_core,
* since that will be the supported, stable interface going forwards.
*/
#include <libzfs_core.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/nvpair.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/zfs_ioctl.h>
static int g_fd;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static int g_refcount;
int
libzfs_core_init(void)
{
(void) pthread_mutex_lock(&g_lock);
if (g_refcount == 0) {
g_fd = open("/dev/zfs", O_RDWR);
if (g_fd < 0) {
(void) pthread_mutex_unlock(&g_lock);
return (errno);
}
}
g_refcount++;
(void) pthread_mutex_unlock(&g_lock);
return (0);
}
void
libzfs_core_fini(void)
{
(void) pthread_mutex_lock(&g_lock);
ASSERT3S(g_refcount, >, 0);
g_refcount--;
if (g_refcount == 0)
(void) close(g_fd);
(void) pthread_mutex_unlock(&g_lock);
}
static int
lzc_ioctl(zfs_ioc_t ioc, const char *name,
nvlist_t *source, nvlist_t **resultp)
{
zfs_cmd_t zc = { 0 };
int error = 0;
char *packed;
size_t size;
ASSERT3S(g_refcount, >, 0);
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
packed = fnvlist_pack(source, &size);
zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
zc.zc_nvlist_src_size = size;
if (resultp != NULL) {
zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
malloc(zc.zc_nvlist_dst_size);
if (zc.zc_nvlist_dst == NULL) {
error = ENOMEM;
goto out;
}
}
while (ioctl(g_fd, ioc, &zc) != 0) {
if (errno == ENOMEM && resultp != NULL) {
free((void *)(uintptr_t)zc.zc_nvlist_dst);
zc.zc_nvlist_dst_size *= 2;
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
malloc(zc.zc_nvlist_dst_size);
if (zc.zc_nvlist_dst == NULL) {
error = ENOMEM;
goto out;
}
} else {
error = errno;
break;
}
}
if (zc.zc_nvlist_dst_filled) {
*resultp = fnvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
zc.zc_nvlist_dst_size);
} else if (resultp != NULL) {
*resultp = NULL;
}
out:
fnvlist_pack_free(packed, size);
free((void *)(uintptr_t)zc.zc_nvlist_dst);
return (error);
}
int
lzc_create(const char *fsname, dmu_objset_type_t type, nvlist_t *props)
{
int error;
nvlist_t *args = fnvlist_alloc();
fnvlist_add_int32(args, "type", type);
if (props != NULL)
fnvlist_add_nvlist(args, "props", props);
error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL);
nvlist_free(args);
return (error);
}
int
lzc_clone(const char *fsname, const char *origin,
nvlist_t *props)
{
int error;
nvlist_t *args = fnvlist_alloc();
fnvlist_add_string(args, "origin", origin);
if (props != NULL)
fnvlist_add_nvlist(args, "props", props);
error = lzc_ioctl(ZFS_IOC_CLONE, fsname, args, NULL);
nvlist_free(args);
return (error);
}
/*
* Creates snapshots.
*
* The keys in the snaps nvlist are the snapshots to be created.
* They must all be in the same pool.
*
* The props nvlist is properties to set. Currently only user properties
* are supported. { user:prop_name -> string value }
*
* The returned results nvlist will have an entry for each snapshot that failed.
* The value will be the (int32) error code.
*
* The return value will be 0 if all snapshots were created, otherwise it will
* be the errno of a (undetermined) snapshot that failed.
*/
int
lzc_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t **errlist)
{
nvpair_t *elem;
nvlist_t *args;
int error;
char pool[MAXNAMELEN];
*errlist = NULL;
/* determine the pool name */
elem = nvlist_next_nvpair(snaps, NULL);
if (elem == NULL)
return (0);
(void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
pool[strcspn(pool, "/@")] = '\0';
args = fnvlist_alloc();
fnvlist_add_nvlist(args, "snaps", snaps);
if (props != NULL)
fnvlist_add_nvlist(args, "props", props);
error = lzc_ioctl(ZFS_IOC_SNAPSHOT, pool, args, errlist);
nvlist_free(args);
return (error);
}
/*
* Destroys snapshots.
*
* The keys in the snaps nvlist are the snapshots to be destroyed.
* They must all be in the same pool.
*
* Snapshots that do not exist will be silently ignored.
*
* If 'defer' is not set, and a snapshot has user holds or clones, the
* destroy operation will fail and none of the snapshots will be
* destroyed.
*
* If 'defer' is set, and a snapshot has user holds or clones, it will be
* marked for deferred destruction, and will be destroyed when the last hold
* or clone is removed/destroyed.
*
* The return value will be 0 if all snapshots were destroyed (or marked for
* later destruction if 'defer' is set) or didn't exist to begin with.
*
* Otherwise the return value will be the errno of a (undetermined) snapshot
* that failed, no snapshots will be destroyed, and the errlist will have an
* entry for each snapshot that failed. The value in the errlist will be
* the (int32) error code.
*/
int
lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist)
{
nvpair_t *elem;
nvlist_t *args;
int error;
char pool[MAXNAMELEN];
/* determine the pool name */
elem = nvlist_next_nvpair(snaps, NULL);
if (elem == NULL)
return (0);
(void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
pool[strcspn(pool, "/@")] = '\0';
args = fnvlist_alloc();
fnvlist_add_nvlist(args, "snaps", snaps);
if (defer)
fnvlist_add_boolean(args, "defer");
error = lzc_ioctl(ZFS_IOC_DESTROY_SNAPS, pool, args, errlist);
nvlist_free(args);
return (error);
}
int
lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
uint64_t *usedp)
{
nvlist_t *args;
nvlist_t *result;
int err;
char fs[MAXNAMELEN];
char *atp;
/* determine the fs name */
(void) strlcpy(fs, firstsnap, sizeof (fs));
atp = strchr(fs, '@');
if (atp == NULL)
return (EINVAL);
*atp = '\0';
args = fnvlist_alloc();
fnvlist_add_string(args, "firstsnap", firstsnap);
err = lzc_ioctl(ZFS_IOC_SPACE_SNAPS, lastsnap, args, &result);
nvlist_free(args);
if (err == 0)
*usedp = fnvlist_lookup_uint64(result, "used");
fnvlist_free(result);
return (err);
}
boolean_t
lzc_exists(const char *dataset)
{
/*
* The objset_stats ioctl is still legacy, so we need to construct our
* own zfs_cmd_t rather than using zfsc_ioctl().
*/
zfs_cmd_t zc = { 0 };
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
return (ioctl(g_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0);
}
/*
* If fromsnap is NULL, a full (non-incremental) stream will be sent.
*/
int
lzc_send(const char *snapname, const char *fromsnap, 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);
err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
nvlist_free(args);
return (err);
}
/*
* If fromsnap is NULL, a full (non-incremental) stream will be estimated.
*/
int
lzc_send_space(const char *snapname, const char *fromsnap, uint64_t *spacep)
{
nvlist_t *args;
nvlist_t *result;
int err;
args = fnvlist_alloc();
if (fromsnap != NULL)
fnvlist_add_string(args, "fromsnap", fromsnap);
err = lzc_ioctl(ZFS_IOC_SEND_SPACE, snapname, args, &result);
nvlist_free(args);
if (err == 0)
*spacep = fnvlist_lookup_uint64(result, "space");
nvlist_free(result);
return (err);
}
static int
recv_read(int fd, void *buf, int ilen)
{
char *cp = buf;
int rv;
int len = ilen;
do {
rv = read(fd, cp, len);
cp += rv;
len -= rv;
} while (rv > 0);
if (rv < 0 || len != 0)
return (EIO);
return (0);
}
/*
* The simplest receive case: receive from the specified fd, creating the
* specified snapshot. Apply the specified properties a "received" properties
* (which can be overridden by locally-set properties). If the stream is a
* clone, its origin snapshot must be specified by 'origin'. The 'force'
* flag will cause the target filesystem to be rolled back or destroyed if
* necessary to receive.
*
* Return 0 on success or an errno on failure.
*
* Note: this interface does not work on dedup'd streams
* (those with DMU_BACKUP_FEATURE_DEDUP).
*/
int
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
boolean_t force, int fd)
{
/*
* The receive ioctl is still legacy, so we need to construct our own
* zfs_cmd_t rather than using zfsc_ioctl().
*/
zfs_cmd_t zc = { 0 };
char *atp;
char *packed = NULL;
size_t size;
dmu_replay_record_t drr;
int error;
ASSERT3S(g_refcount, >, 0);
/* zc_name is name of containing filesystem */
(void) strlcpy(zc.zc_name, snapname, sizeof (zc.zc_name));
atp = strchr(zc.zc_name, '@');
if (atp == NULL)
return (EINVAL);
*atp = '\0';
/* if the fs does not exist, try its parent. */
if (!lzc_exists(zc.zc_name)) {
char *slashp = strrchr(zc.zc_name, '/');
if (slashp == NULL)
return (ENOENT);
*slashp = '\0';
}
/* zc_value is full name of the snapshot to create */
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
if (props != NULL) {
/* zc_nvlist_src is props to set */
packed = fnvlist_pack(props, &size);
zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
zc.zc_nvlist_src_size = size;
}
/* zc_string is name of clone origin (if DRR_FLAG_CLONE) */
if (origin != NULL)
(void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string));
/* zc_begin_record is non-byteswapped BEGIN record */
error = recv_read(fd, &drr, sizeof (drr));
if (error != 0)
goto out;
zc.zc_begin_record = drr.drr_u.drr_begin;
/* zc_cookie is fd to read from */
zc.zc_cookie = fd;
/* zc guid is force flag */
zc.zc_guid = force;
/* zc_cleanup_fd is unused */
zc.zc_cleanup_fd = -1;
error = ioctl(g_fd, ZFS_IOC_RECV, &zc);
if (error != 0)
error = errno;
out:
if (packed != NULL)
fnvlist_pack_free(packed, size);
free((void*)(uintptr_t)zc.zc_nvlist_dst);
return (error);
}

View File

@ -0,0 +1,62 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
#ifndef _LIBZFS_CORE_H
#define _LIBZFS_CORE_H
#include <libnvpair.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/fs/zfs.h>
#ifdef __cplusplus
extern "C" {
#endif
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_snaprange_space(const char *firstsnap, const char *lastsnap,
uint64_t *usedp);
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);
boolean_t lzc_exists(const char *dataset);
#ifdef __cplusplus
}
#endif
#endif /* _LIBZFS_CORE_H */

View File

@ -871,6 +871,12 @@ crgetuid(cred_t *cr)
return (0); return (0);
} }
uid_t
crgetruid(cred_t *cr)
{
return (0);
}
gid_t gid_t
crgetgid(cred_t *cr) crgetgid(cred_t *cr)
{ {

View File

@ -286,6 +286,7 @@ extern void rw_exit(krwlock_t *rwlp);
#define rw_downgrade(rwlp) do { } while (0) #define rw_downgrade(rwlp) do { } while (0)
extern uid_t crgetuid(cred_t *cr); extern uid_t crgetuid(cred_t *cr);
extern uid_t crgetruid(cred_t *cr);
extern gid_t crgetgid(cred_t *cr); extern gid_t crgetgid(cred_t *cr);
extern int crgetngroups(cred_t *cr); extern int crgetngroups(cred_t *cr);
extern gid_t *crgetgroups(cred_t *cr); extern gid_t *crgetgroups(cred_t *cr);

View File

@ -39,7 +39,7 @@ zfs \- configures ZFS file systems
.LP .LP
.nf .nf
\fBzfs\fR \fBsnapshot\fR [\fB-r\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR]... \fBzfs\fR \fBsnapshot\fR [\fB-r\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR]...
\fIfilesystem@snapname\fR|\fIvolume@snapname\fR \fIfilesystem@snapname\fR|\fIvolume@snapname\fR...
.fi .fi
.LP .LP
@ -1837,13 +1837,14 @@ behavior for mounted file systems in use.
.ne 2 .ne 2
.na .na
\fB\fBzfs snapshot\fR [\fB-r\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ... \fB\fBzfs snapshot\fR [\fB-r\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ...
\fIfilesystem@snapname\fR|\fIvolume@snapname\fR\fR \fIfilesystem@snapname\fR|\fIvolume@snapname\fR\fR...
.ad .ad
.sp .6 .sp .6
.RS 4n .RS 4n
Creates a snapshot with the given name. All previous modifications by Creates snapshots with the given names. All previous modifications by
successful system calls to the file system are part of the snapshot. See the successful system calls to the file system are part of the snapshots.
"Snapshots" section for details. Snapshots are taken atomically, so that all snapshots correspond to the same
moment in time. See the "Snapshots" section for details.
.sp .sp
.ne 2 .ne 2
.na .na
@ -1851,9 +1852,7 @@ successful system calls to the file system are part of the snapshot. See the
.ad .ad
.sp .6 .sp .6
.RS 4n .RS 4n
Recursively create snapshots of all descendent datasets. Snapshots are taken Recursively create snapshots of all descendent datasets
atomically, so that all recursive snapshots correspond to the same moment in
time.
.RE .RE
.sp .sp