Print this page

        

@@ -75,10 +75,11 @@
 static int zfs_do_send(int argc, char **argv);
 static int zfs_do_receive(int argc, char **argv);
 static int zfs_do_promote(int argc, char **argv);
 static int zfs_do_allow(int argc, char **argv);
 static int zfs_do_unallow(int argc, char **argv);
+static int zfs_do_key(int argc, char **argv);
 
 /*
  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
  */
 

@@ -114,11 +115,12 @@
         HELP_SHARE,
         HELP_SNAPSHOT,
         HELP_UNMOUNT,
         HELP_UNSHARE,
         HELP_ALLOW,
-        HELP_UNALLOW
+        HELP_UNALLOW,
+        HELP_KEY
 } zfs_help_t;
 
 typedef struct zfs_command {
         const char      *name;
         int             (*func)(int argc, char **argv);

@@ -160,10 +162,12 @@
         { "receive",    zfs_do_receive,         HELP_RECEIVE            },
         { NULL },
         { "allow",      zfs_do_allow,           HELP_ALLOW              },
         { NULL },
         { "unallow",    zfs_do_unallow,         HELP_UNALLOW            },
+        { NULL },
+        { "key",        zfs_do_key,             HELP_KEY                },
 };
 
 #define NCOMMAND        (sizeof (command_table) / sizeof (command_table[0]))
 
 zfs_command_t *current_command;

@@ -248,10 +252,13 @@
                     "<filesystem|volume>\n"
                     "\tunallow [-r] -c [<perm|@setname>[,...]] "
                     "<filesystem|volume>\n"
                     "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
                     "<filesystem|volume>\n"));
+        case HELP_KEY:
+                return (gettext("\tkey <-l | -u | -c [ -o <property=value>]> "
+                    "<-a | filesystem>\n"));
         }
 
         abort();
         /* NOTREACHED */
 }

@@ -3059,11 +3066,13 @@
                 }
 
                 /*
                  * Ignore any filesystems which don't apply to us. This
                  * includes those with a legacy mountpoint, or those with
-                 * legacy share options.
+                 * legacy share options.  We also have to ignore those
+                 * that are encrypted that don't currently have their
+                 * key available.
                  */
                 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
                     sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
                     sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);

@@ -3128,11 +3137,27 @@
                         return (1);
                 } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
                         return (0);
                 }
 
+
                 /*
+                 * Only need to check for ZFS_CRYPT_KEY_UNAVAILABLE since
+                 * datasets that aren't encrypted have a keystatus of
+                 * ZFS_CRYPT_KEY_UNDEFINED.
+                 */
+                if (zfs_mount_crypto_check(zhp) != 0) {
+                        if (!explicit)
+                                return (0);
+
+                        (void) fprintf(stderr, gettext("cannot %s '%s': "
+                            "encryption key unavailable\n"), cmdname,
+                            zfs_get_name(zhp));
+                        return (1);
+                }
+
+                /*
                  * At this point, we have verified that the mountpoint and/or
                  * shareopts are appropriate for auto management. If the
                  * filesystem is already mounted or shared, return (failing
                  * for explicit requests); otherwise mount or share the
                  * filesystem.

@@ -3348,11 +3373,13 @@
         /* check number of arguments */
         if (do_all) {
                 zfs_handle_t **dslist = NULL;
                 size_t i, count = 0;
                 char *protocol = NULL;
+                char bypass[ZPOOL_MAXPROPLEN] = { 0 };
 
+
                 if (op == OP_MOUNT) {
                         types = ZFS_TYPE_FILESYSTEM;
                 } else if (argc > 0) {
                         if (strcmp(argv[0], "nfs") == 0 ||
                             strcmp(argv[0], "smb") == 0) {

@@ -3382,16 +3409,45 @@
                         return (0);
 
                 qsort(dslist, count, sizeof (void *), dataset_cmp);
 
                 for (i = 0; i < count; i++) {
+
                         if (verbose)
                                 report_mount_progress(i, count);
 
-                        if (share_mount_one(dslist[i], op, flags, protocol,
-                            B_FALSE, options) != 0)
+                        /*
+                         * If bypass has a dataset value, then we need to skip
+                         * any datasets that are underneath it.
+                         */
+                        if (bypass[0] != NULL) {
+                                int len = strlen(bypass);
+                                char *ds_name = (char *)zfs_get_name(dslist[i]);
+
+                                if (strncmp(bypass, ds_name, len) == 0 &&
+                                    (strlen(ds_name) > len) &&
+                                    ds_name[len] == '/') {
+                                        zfs_close(dslist[i]);
+                                        continue;
+                                } else
+                                        bypass[0] = '\0';
+                        }
+
+                        /*
+                         * Check if the dataset has a key before loading, if
+                         * no, then store it in 'bypass'.
+                         */
+                        if (zfs_mount_crypto_check(dslist[i])) {
+                                (void) strlcpy(bypass, zfs_get_name(dslist[i]),
+                                    ZPOOL_MAXPROPLEN);
+                        } else {
+                                if (share_mount_one(dslist[i], op, flags,
+                                    protocol, B_FALSE, options) != 0) {
                                 ret = 1;
+                                }
+                        }
+
                         zfs_close(dslist[i]);
                 }
 
                 free(dslist);
         } else if (argc == 0) {

@@ -4077,10 +4133,207 @@
         }
 
         return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
 }
 
+int
+zfs_do_key(int argc, char **argv)
+{
+        int error = 1, options = 0;
+        nvlist_t *props = NULL;
+        char c, *propname, *propval = NULL;
+        boolean_t load = B_FALSE, unload = B_FALSE, change = B_FALSE;
+        boolean_t do_all = B_FALSE;
+        char *strval;
+        zfs_handle_t **dslist = NULL, *zhp = NULL;
+        uint_t count;
+        zfs_prop_t zprop;
+
+        while ((c = getopt(argc, argv, "aluco:")) != -1) {
+                switch (c) {
+                case 'a':
+                        do_all = B_TRUE;
+                        break;
+
+                case 'l':
+                        load = B_TRUE;
+                        break;
+
+                case 'u':
+                        unload = B_TRUE;
+                        break;
+
+                case 'c':
+                        change = B_TRUE;
+                        break;
+
+                case 'o':
+                        /* Key change is the only command that allows options */
+                        if (change != B_TRUE) {
+                                (void) fprintf(stderr, gettext("Property "
+                                    "options only allowed during key "
+                                    "change.\n"));
+                                usage(B_FALSE);
+                                goto error;
+                        }
+
+                        propname = optarg;
+                        if ((propval = strchr(optarg, '=')) == NULL) {
+                                (void) fprintf(stderr, gettext("missing "
+                                    "'=' for -o option\n"));
+                                goto error;
+                        }
+
+                        *propval = '\0';
+                        propval++;
+
+                        zprop = zfs_name_to_prop(propname);
+                        switch (zprop) {
+                        case ZFS_PROP_KEYSOURCE:
+                        case ZFS_PROP_KEYSCOPE:
+                                break;
+
+                        default:
+                                (void) fprintf(stderr, gettext("Invalid "
+                                    "property for key operation: '%s'\n"),
+                                    propname);
+                                goto error;
+                        };
+
+                        if (props == NULL &&
+                            nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+                                (void) fprintf(stderr, gettext("internal "
+                                    "error: out of memory\n"));
+                                goto error;
+                        }
+
+                        if (nvlist_lookup_string(props, propname,
+                            &strval) == 0) {
+                                (void) fprintf(stderr, gettext("property '%s' "
+                                    "specified multiple times\n"), propname);
+                                goto error;
+                        }
+                        if (nvlist_add_string(props, propname, propval) != 0) {
+                                (void) fprintf(stderr, gettext("internal "
+                                    "error: out of memory\n"));
+                                goto error;
+                        }
+
+                        options += 2;
+                        break;
+
+                case '?':
+                default:
+                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                            optopt);
+                        usage(B_FALSE);
+
+                }
+        }
+
+        if (!change && props != NULL)
+                (void) fprintf(stderr, gettext("Properties are not allowed to "
+                    "be used in this command.\n"));
+
+        if (((load || unload) && (argc > 3)) ||
+            (change && ((argc - options) > 3))) {
+                        (void) fprintf(stderr,
+                            gettext("too many arguments\n"));
+                        usage(B_FALSE);
+                        goto error;
+        } else if ((load || unload) && (argc < 3)) {
+                        (void) fprintf(stderr, gettext("missing dataset "
+                            "argument (specify -a for all)\n"));
+                        usage(B_FALSE);
+                        goto error;
+        } else if (change && ((argc - options) < 3)) {
+                        (void) fprintf(stderr, gettext("missing dataset "
+                            "argument\n"));
+                        usage(B_FALSE);
+                        goto error;
+        }
+
+        if (do_all == B_FALSE) {
+                zhp = zfs_open(g_zfs, argv[argc - 1],
+                    ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME);
+                if (zhp == NULL)
+                        goto error;
+
+        } else if (change) {
+                /* We don't support do_all in a change operation */
+
+                (void) fprintf(stderr, gettext("cannot use '-a' with "
+                    "change operation.\n"));
+                usage(B_FALSE);
+                goto error;
+
+        } else {
+                get_all_datasets(ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
+                    &dslist, &count, B_FALSE);
+                if (count == 0)
+                        return (0);
+
+                qsort(dslist, count, sizeof (void *), dataset_cmp);
+        }
+
+        if (load) {
+                if (do_all) {
+                        int i;
+                        zfs_crypt_t *cry = NULL;
+
+                        cry = calloc(1, sizeof (zfs_crypt_t));
+                        for (i = 0; i < count; i++) {
+                                if (zfs_is_encrypted(dslist[i])) {
+                                        zfs_set_libzfs_cry(dslist[i], cry);
+                                        (void) zfs_load_key(dslist[i]);
+                                        bzero(cry, sizeof (zfs_crypt_t));
+                                }
+                                zfs_close(dslist[i]);
+                        }
+
+                        free(cry);
+                        free(dslist);
+                        error = 0;
+
+                } else
+                        error = zfs_cmd_key_load(zhp);
+
+        } else if (unload) {
+                if (do_all) {
+                        int i;
+
+                        /* Do in reverse order so we can unmount easily */
+                        for (i = count - 1; i > 0; i--) {
+                                if (zfs_is_encrypted(dslist[i]))
+                                        (void) zfs_unload_key(dslist[i]);
+
+                                zfs_close(dslist[i]);
+                        }
+
+                        free(dslist);
+                        error = 0;
+
+                } else
+                        error = zfs_cmd_key_unload(zhp);
+
+        } else if (change) {
+                error = zfs_cmd_key_change(zhp, props);
+
+        } else
+                usage(B_FALSE);
+
+        if (zhp != NULL)
+                zfs_close(zhp);
+
+error:
+        if (props != NULL) {
+                nvlist_free(props);
+        }
+        return (error);
+}
+
+
 static int
 volcheck(zpool_handle_t *zhp, void *data)
 {
         boolean_t isinit = *((boolean_t *)data);