Print this page
2882 implement libzfs_core
2883 changing "canmount" property to "on" should not always remount dataset
2900 "zfs snapshot" should be able to create multiple, arbitrary snapshots at once
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Chris Siden <christopher.siden@delphix.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Bill Pijewski <wdp@joyent.com>
Reviewed by: Dan Kruchinin <dan.kruchinin@gmail.com>
*** 54,63 ****
--- 54,64 ----
#include <sys/fs/zfs.h>
#include <sys/types.h>
#include <time.h>
#include <libzfs.h>
+ #include <libzfs_core.h>
#include <zfs_prop.h>
#include <zfs_deleg.h>
#include <libuutil.h>
#include <aclutils.h>
#include <directory.h>
*** 68,77 ****
--- 69,79 ----
libzfs_handle_t *g_zfs;
static FILE *mnttab_file;
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_create(int argc, char **argv);
static int zfs_do_destroy(int argc, char **argv);
static int zfs_do_get(int argc, char **argv);
*** 257,267 ****
"<filesystem|volume|snapshot> ...\n"));
case HELP_SHARE:
return (gettext("\tshare <-a | filesystem>\n"));
case HELP_SNAPSHOT:
return (gettext("\tsnapshot [-r] [-o property=value] ... "
! "<filesystem@snapname|volume@snapname>\n"));
case HELP_UNMOUNT:
return (gettext("\tunmount [-f] "
"<-a | filesystem|mountpoint>\n"));
case HELP_UNSHARE:
return (gettext("\tunshare "
--- 259,269 ----
"<filesystem|volume|snapshot> ...\n"));
case HELP_SHARE:
return (gettext("\tshare <-a | filesystem>\n"));
case HELP_SNAPSHOT:
return (gettext("\tsnapshot [-r] [-o property=value] ... "
! "<filesystem@snapname|volume@snapname> ...\n"));
case HELP_UNMOUNT:
return (gettext("\tunmount [-f] "
"<-a | filesystem|mountpoint>\n"));
case HELP_UNSHARE:
return (gettext("\tunshare "
*** 886,898 ****
boolean_t cb_parsable;
boolean_t cb_dryrun;
nvlist_t *cb_nvl;
/* first snap in contiguous run */
! zfs_handle_t *cb_firstsnap;
/* previous snap in contiguous run */
! zfs_handle_t *cb_prevsnap;
int64_t cb_snapused;
char *cb_snapspec;
} destroy_cbdata_t;
/*
--- 888,900 ----
boolean_t cb_parsable;
boolean_t cb_dryrun;
nvlist_t *cb_nvl;
/* first snap in contiguous run */
! char *cb_firstsnap;
/* previous snap in contiguous run */
! char *cb_prevsnap;
int64_t cb_snapused;
char *cb_snapspec;
} destroy_cbdata_t;
/*
*** 1002,1016 ****
const char *name = zfs_get_name(zhp);
int err = 0;
if (nvlist_exists(cb->cb_nvl, name)) {
if (cb->cb_firstsnap == NULL)
! cb->cb_firstsnap = zfs_handle_dup(zhp);
if (cb->cb_prevsnap != NULL)
! zfs_close(cb->cb_prevsnap);
/* this snap continues the current range */
! cb->cb_prevsnap = zfs_handle_dup(zhp);
if (cb->cb_verbose) {
if (cb->cb_parsable) {
(void) printf("destroy\t%s\n", name);
} else if (cb->cb_dryrun) {
(void) printf(gettext("would destroy %s\n"),
--- 1004,1020 ----
const char *name = zfs_get_name(zhp);
int err = 0;
if (nvlist_exists(cb->cb_nvl, name)) {
if (cb->cb_firstsnap == NULL)
! cb->cb_firstsnap = strdup(name);
if (cb->cb_prevsnap != NULL)
! free(cb->cb_prevsnap);
/* this snap continues the current range */
! cb->cb_prevsnap = strdup(name);
! if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
! nomem();
if (cb->cb_verbose) {
if (cb->cb_parsable) {
(void) printf("destroy\t%s\n", name);
} else if (cb->cb_dryrun) {
(void) printf(gettext("would destroy %s\n"),
*** 1021,1036 ****
}
}
} else if (cb->cb_firstsnap != NULL) {
/* end of this range */
uint64_t used = 0;
! err = zfs_get_snapused_int(cb->cb_firstsnap,
cb->cb_prevsnap, &used);
cb->cb_snapused += used;
! zfs_close(cb->cb_firstsnap);
cb->cb_firstsnap = NULL;
! zfs_close(cb->cb_prevsnap);
cb->cb_prevsnap = NULL;
}
zfs_close(zhp);
return (err);
}
--- 1025,1040 ----
}
}
} else if (cb->cb_firstsnap != NULL) {
/* end of this range */
uint64_t used = 0;
! err = lzc_snaprange_space(cb->cb_firstsnap,
cb->cb_prevsnap, &used);
cb->cb_snapused += used;
! free(cb->cb_firstsnap);
cb->cb_firstsnap = NULL;
! free(cb->cb_prevsnap);
cb->cb_prevsnap = NULL;
}
zfs_close(zhp);
return (err);
}
*** 1043,1059 ****
assert(cb->cb_prevsnap == NULL);
err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
if (cb->cb_firstsnap != NULL) {
uint64_t used = 0;
if (err == 0) {
! err = zfs_get_snapused_int(cb->cb_firstsnap,
cb->cb_prevsnap, &used);
}
cb->cb_snapused += used;
! zfs_close(cb->cb_firstsnap);
cb->cb_firstsnap = NULL;
! zfs_close(cb->cb_prevsnap);
cb->cb_prevsnap = NULL;
}
return (err);
}
--- 1047,1063 ----
assert(cb->cb_prevsnap == NULL);
err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
if (cb->cb_firstsnap != NULL) {
uint64_t used = 0;
if (err == 0) {
! err = lzc_snaprange_space(cb->cb_firstsnap,
cb->cb_prevsnap, &used);
}
cb->cb_snapused += used;
! free(cb->cb_firstsnap);
cb->cb_firstsnap = NULL;
! free(cb->cb_prevsnap);
cb->cb_prevsnap = NULL;
}
return (err);
}
*** 1902,1914 ****
"%llu", cb->cb_version);
if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
/*
* If they did "zfs upgrade -a", then we could
* be doing ioctls to different pools. We need
! * to log this history once to each pool.
*/
! verify(zpool_stage_history(g_zfs, history_str) == 0);
}
if (zfs_prop_set(zhp, "version", verstr) == 0)
cb->cb_numupgraded++;
else
cb->cb_numfailed++;
--- 1906,1920 ----
"%llu", cb->cb_version);
if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
/*
* If they did "zfs 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;
}
if (zfs_prop_set(zhp, "version", verstr) == 0)
cb->cb_numupgraded++;
else
cb->cb_numfailed++;
*** 3422,3457 ****
ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
return (ret);
}
/*
* zfs snapshot [-r] [-o prop=value] ... <fs@snap>
*
* Creates a snapshot with the given name. While functionally equivalent to
* 'zfs create', it is a separate command to differentiate intent.
*/
static int
zfs_do_snapshot(int argc, char **argv)
{
- boolean_t recursive = B_FALSE;
int ret = 0;
char c;
nvlist_t *props;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
nomem();
/* check options */
while ((c = getopt(argc, argv, "ro:")) != -1) {
switch (c) {
case 'o':
if (parseprop(props))
return (1);
break;
case 'r':
! recursive = B_TRUE;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
goto usage;
--- 3428,3493 ----
ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
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>
*
* Creates a snapshot with the given name. While functionally equivalent to
* 'zfs create', it is a separate command to differentiate intent.
*/
static int
zfs_do_snapshot(int argc, char **argv)
{
int ret = 0;
char c;
nvlist_t *props;
+ snap_cbdata_t sd = { 0 };
+ boolean_t multiple_snaps = B_FALSE;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
nomem();
+ if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
+ nomem();
/* check options */
while ((c = getopt(argc, argv, "ro:")) != -1) {
switch (c) {
case 'o':
if (parseprop(props))
return (1);
break;
case 'r':
! sd.sd_recursive = B_TRUE;
! multiple_snaps = B_TRUE;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
goto usage;
*** 3464,3485 ****
/* check number of arguments */
if (argc < 1) {
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
goto usage;
}
! if (argc > 1) {
! (void) fprintf(stderr, gettext("too many arguments\n"));
goto usage;
}
! ret = zfs_snapshot(g_zfs, argv[0], recursive, props);
nvlist_free(props);
! if (ret && recursive)
(void) fprintf(stderr, gettext("no snapshots were created\n"));
return (ret != 0);
usage:
nvlist_free(props);
usage(B_FALSE);
return (-1);
}
--- 3500,3538 ----
/* check number of arguments */
if (argc < 1) {
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
goto usage;
}
!
! if (argc > 1)
! 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_nvl(g_zfs, sd.sd_nvl, props);
! nvlist_free(sd.sd_nvl);
nvlist_free(props);
! if (ret != 0 && multiple_snaps)
(void) fprintf(stderr, gettext("no snapshots were created\n"));
return (ret != 0);
usage:
+ nvlist_free(sd.sd_nvl);
nvlist_free(props);
usage(B_FALSE);
return (-1);
}
*** 6477,6488 ****
(void) fprintf(stderr, gettext("internal error: failed to "
"initialize ZFS library\n"));
return (1);
}
! zpool_set_history_str("zfs", argc, argv, history_str);
! verify(zpool_stage_history(g_zfs, history_str) == 0);
libzfs_print_on_error(g_zfs, B_TRUE);
if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
(void) fprintf(stderr, gettext("internal error: unable to "
--- 6530,6540 ----
(void) fprintf(stderr, gettext("internal error: failed to "
"initialize ZFS library\n"));
return (1);
}
! zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
libzfs_print_on_error(g_zfs, B_TRUE);
if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
(void) fprintf(stderr, gettext("internal error: unable to "
*** 6547,6556 ****
--- 6599,6611 ----
libzfs_mnttab_cache(g_zfs, B_FALSE);
}
(void) fclose(mnttab_file);
+ if (ret == 0 && log_history)
+ (void) zpool_log_history(g_zfs, history_str);
+
libzfs_fini(g_zfs);
/*
* The 'ZFS_ABORT' environment variable causes us to dump core on exit
* for the purposes of running ::findleaks.