--- old/usr/src/cmd/zfs/zfs_main.c Tue Feb 3 13:17:19 2009 +++ new/usr/src/cmd/zfs/zfs_main.c Tue Feb 3 13:17:18 2009 @@ -77,6 +77,7 @@ 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. @@ -116,7 +117,8 @@ HELP_UNMOUNT, HELP_UNSHARE, HELP_ALLOW, - HELP_UNALLOW + HELP_UNALLOW, + HELP_KEY } zfs_help_t; typedef struct zfs_command { @@ -162,6 +164,8 @@ { "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])) @@ -250,6 +254,9 @@ "\n" "\tunallow [-r] -s @setname [[,...]] " "\n")); + case HELP_KEY: + return (gettext("\tkey <-l | -u | -c [ -o ]> " + "<-a | filesystem>\n")); } abort(); @@ -3061,7 +3068,9 @@ /* * 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); @@ -3130,7 +3139,23 @@ 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 @@ -3350,7 +3375,9 @@ 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) { @@ -3384,12 +3411,41 @@ 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) - ret = 1; + /* + * 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]); } @@ -4079,6 +4135,203 @@ 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) {