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.