/* * Iterate over root datasets, calling the given function for each. The zfs * handle passed each time must be explicitly closed by the callback. */ int zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data) { config_node_t *cn; zfs_handle_t *zhp; int ret; if (namespace_reload(hdl) != 0) return (-1); for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL; 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) continue; if ((ret = func(zhp, data)) != 0) return (ret); } return (0); }
int zfs_crypto_clone_check(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, char *parent_name, nvlist_t *props) { int ret; char errbuf[1024]; zfs_handle_t *pzhp = NULL; uint64_t pcrypt, ocrypt; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Encryption clone error")); /* * No encryption properties should be specified. They will all be * inherited from the origin dataset. */ if (nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_KEYFORMAT)) || nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_KEYLOCATION)) || nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION)) || nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS))) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption properties must inherit from origin dataset.")); goto out; } /* get a reference to parent dataset, should never be NULL */ pzhp = make_dataset_handle(hdl, parent_name); if (pzhp == NULL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to lookup parent.")); return (ENOENT); } /* Lookup parent's crypt */ pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); ocrypt = zfs_prop_get_int(origin_zhp, ZFS_PROP_ENCRYPTION); /* all children of encrypted parents must be encrypted */ if (pcrypt != ZIO_CRYPT_OFF && ocrypt == ZIO_CRYPT_OFF) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Cannot create unencrypted clone as a child " "of encrypted parent.")); goto out; } zfs_close(pzhp); return (0); out: if (pzhp != NULL) zfs_close(pzhp); return (ret); }
/* * Iterate over all zvols in the poool and remove any minor nodes. */ int zpool_remove_zvol_links(zpool_handle_t *zhp) { zfs_handle_t *zfp; int ret; /* * If the pool is unavailable, just return success. */ if ((zfp = make_dataset_handle(zhp->zpool_hdl, zhp->zpool_name)) == NULL) return (0); ret = zfs_iter_children(zfp, do_zvol, (void *)B_FALSE); zfs_close(zfp); return (ret); }
/* * Unshare and unmount all datasets within the given pool. We don't want to * rely on traversing the DSL to discover the filesystems within the pool, * because this may be expensive (if not all of them are mounted), and can fail * arbitrarily (on I/O error, for example). Instead, we walk /etc/mtab and * gather all the filesystems that are currently mounted. */ int zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) { int used, alloc; struct mnttab entry; size_t namelen; char **mountpoints = NULL; zfs_handle_t **datasets = NULL; libzfs_handle_t *hdl = zhp->zpool_hdl; int i; int ret = -1; int flags = (force ? MS_FORCE : 0); namelen = strlen(zhp->zpool_name); rewind(hdl->libzfs_mnttab); used = alloc = 0; while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { /* * Ignore non-ZFS entries. */ if (entry.mnt_fstype == NULL || strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) continue; /* * Ignore filesystems not within this pool. */ if (entry.mnt_mountp == NULL || strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || (entry.mnt_special[namelen] != '/' && entry.mnt_special[namelen] != '\0')) continue; /* * At this point we've found a filesystem within our pool. Add * it to our growing list. */ if (used == alloc) { if (alloc == 0) { if ((mountpoints = zfs_alloc(hdl, 8 * sizeof (void *))) == NULL) goto out; if ((datasets = zfs_alloc(hdl, 8 * sizeof (void *))) == NULL) goto out; alloc = 8; } else { void *ptr; if ((ptr = zfs_realloc(hdl, mountpoints, alloc * sizeof (void *), alloc * 2 * sizeof (void *))) == NULL) goto out; mountpoints = ptr; if ((ptr = zfs_realloc(hdl, datasets, alloc * sizeof (void *), alloc * 2 * sizeof (void *))) == NULL) goto out; datasets = ptr; alloc *= 2; } } if ((mountpoints[used] = zfs_strdup(hdl, entry.mnt_mountp)) == NULL) goto out; /* * This is allowed to fail, in case there is some I/O error. It * is only used to determine if we need to remove the underlying * mountpoint, so failure is not fatal. */ datasets[used] = make_dataset_handle(hdl, entry.mnt_special); used++; } /* * At this point, we have the entire list of filesystems, so sort it by * mountpoint. */ qsort(mountpoints, used, sizeof (char *), mountpoint_compare); /* * Walk through and first unshare everything. */ for (i = 0; i < used; i++) { zfs_share_proto_t *curr_proto; for (curr_proto = share_all_proto; *curr_proto != PROTO_END; curr_proto++) { if (is_shared(hdl, mountpoints[i], *curr_proto) && unshare_one(hdl, mountpoints[i], mountpoints[i], *curr_proto) != 0) goto out; } } /* * Now unmount everything, removing the underlying directories as * appropriate. */ for (i = 0; i < used; i++) { if (unmount_one(hdl, mountpoints[i], flags) != 0) goto out; } for (i = 0; i < used; i++) { if (datasets[i]) remove_mountpoint(datasets[i]); } ret = 0; out: for (i = 0; i < used; i++) { if (datasets[i]) zfs_close(datasets[i]); free(mountpoints[i]); } free(datasets); free(mountpoints); return (ret); }
/* * spec is a string like "A,B%C,D" * * <snaps>, where <snaps> can be: * <snap> (single snapshot) * <snap>%<snap> (range of snapshots, inclusive) * %<snap> (range of snapshots, starting with earliest) * <snap>% (range of snapshots, ending with last) * % (all snapshots) * <snaps>[,...] (comma separated list of the above) * * If a snapshot can not be opened, continue trying to open the others, but * return ENOENT at the end. */ int zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig, zfs_iter_f func, void *arg) { char *buf, *comma_separated, *cp; int err = 0; int ret = 0; buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig); cp = buf; while ((comma_separated = strsep(&cp, ",")) != NULL) { char *pct = strchr(comma_separated, '%'); if (pct != NULL) { snapspec_arg_t ssa = { 0 }; ssa.ssa_func = func; ssa.ssa_arg = arg; if (pct == comma_separated) ssa.ssa_seenfirst = B_TRUE; else ssa.ssa_first = comma_separated; *pct = '\0'; ssa.ssa_last = pct + 1; /* * If there is a lastname specified, make sure it * exists. */ if (ssa.ssa_last[0] != '\0') { char snapname[ZFS_MAXNAMELEN]; (void) snprintf(snapname, sizeof (snapname), "%s@%s", zfs_get_name(fs_zhp), ssa.ssa_last); if (!zfs_dataset_exists(fs_zhp->zfs_hdl, snapname, ZFS_TYPE_SNAPSHOT)) { ret = ENOENT; continue; } } err = zfs_iter_snapshots_sorted(fs_zhp, snapspec_cb, &ssa); if (ret == 0) ret = err; if (ret == 0 && (!ssa.ssa_seenfirst || (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) { ret = ENOENT; } } else { char snapname[ZFS_MAXNAMELEN]; zfs_handle_t *snap_zhp; (void) snprintf(snapname, sizeof (snapname), "%s@%s", zfs_get_name(fs_zhp), comma_separated); snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl, snapname); if (snap_zhp == NULL) { ret = ENOENT; continue; } err = func(snap_zhp, arg); if (ret == 0) ret = err; } } free(buf); return (ret); }
int zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, nvlist_t *pool_props, uint8_t **wkeydata_out, uint_t *wkeylen_out) { int ret; char errbuf[1024]; uint64_t crypt = ZIO_CRYPT_INHERIT, pcrypt = ZIO_CRYPT_INHERIT; uint64_t keyformat = ZFS_KEYFORMAT_NONE; char *keylocation = NULL; zfs_handle_t *pzhp = NULL; uint8_t *wkeydata = NULL; uint_t wkeylen = 0; boolean_t local_crypt = B_TRUE; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Encryption create error")); /* lookup crypt from props */ ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt); if (ret != 0) local_crypt = B_FALSE; /* lookup key location and format from props */ (void) nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat); (void) nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation); if (parent_name != NULL) { /* get a reference to parent dataset */ pzhp = make_dataset_handle(hdl, parent_name); if (pzhp == NULL) { ret = ENOENT; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to lookup parent.")); goto out; } /* Lookup parent's crypt */ pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); /* Params require the encryption feature */ if (!encryption_feature_is_enabled(pzhp->zpool_hdl)) { if (proplist_has_encryption_props(props)) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption feature not enabled.")); goto out; } ret = 0; goto out; } } else { /* * special case for root dataset where encryption feature * feature won't be on disk yet */ if (!nvlist_exists(pool_props, "feature@encryption")) { if (proplist_has_encryption_props(props)) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption feature not enabled.")); goto out; } ret = 0; goto out; } pcrypt = ZIO_CRYPT_OFF; } /* Check for encryption being explicitly truned off */ if (crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid encryption value. Dataset must be encrypted.")); goto out; } /* Get the inherited encryption property if we don't have it locally */ if (!local_crypt) crypt = pcrypt; /* * At this point crypt should be the actual encryption value. If * encryption is off just verify that no encryption properties have * been specified and return. */ if (crypt == ZIO_CRYPT_OFF) { if (proplist_has_encryption_props(props)) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption must be turned on to set encryption " "properties.")); goto out; } ret = 0; goto out; } /* * If we have a parent crypt it is valid to specify encryption alone. * This will result in a child that is encrypted with the chosen * encryption suite that will also inherit the parent's key. If * the parent is not encrypted we need an encryption suite provided. */ if (pcrypt == ZIO_CRYPT_OFF && keylocation == NULL && keyformat == ZFS_KEYFORMAT_NONE) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Keyformat required for new encryption root.")); goto out; } /* * Specifying a keylocation implies this will be a new encryption root. * Check that a keyformat is also specified. */ if (keylocation != NULL && keyformat == ZFS_KEYFORMAT_NONE) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Keyformat required for new encryption root.")); goto out; } /* default to prompt if no keylocation is specified */ if (keyformat != ZFS_KEYFORMAT_NONE && keylocation == NULL) { keylocation = "prompt"; ret = nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_KEYLOCATION), keylocation); if (ret != 0) goto out; } /* * If a local key is provided, this dataset will be a new * encryption root. Populate the encryption params. */ if (keylocation != NULL) { ret = populate_create_encryption_params_nvlists(hdl, NULL, B_FALSE, keyformat, keylocation, props, &wkeydata, &wkeylen); if (ret != 0) goto out; } if (pzhp != NULL) zfs_close(pzhp); *wkeydata_out = wkeydata; *wkeylen_out = wkeylen; return (0); out: if (pzhp != NULL) zfs_close(pzhp); if (wkeydata != NULL) free(wkeydata); *wkeydata_out = NULL; *wkeylen_out = 0; return (ret); }
int zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey) { int ret; char errbuf[1024]; boolean_t is_encroot; nvlist_t *props = NULL; uint8_t *wkeydata = NULL; uint_t wkeylen = 0; dcp_cmd_t cmd = (inheritkey) ? DCP_CMD_INHERIT : DCP_CMD_NEW_KEY; uint64_t crypt, pcrypt, keystatus, pkeystatus; uint64_t keyformat = ZFS_KEYFORMAT_NONE; zfs_handle_t *pzhp = NULL; char *keylocation = NULL; char origin_name[MAXNAMELEN]; char prop_keylocation[MAXNAMELEN]; char parent_name[ZFS_MAX_DATASET_NAME_LEN]; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Key change error")); /* check that encryption is enabled for the pool */ if (!encryption_feature_is_enabled(zhp->zpool_hdl)) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Encryption feature not enabled.")); ret = EINVAL; goto error; } /* get crypt from dataset */ crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); if (crypt == ZIO_CRYPT_OFF) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Dataset not encrypted.")); ret = EINVAL; goto error; } /* get the encryption root of the dataset */ ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to get encryption root for '%s'."), zfs_get_name(zhp)); goto error; } /* Clones use their origin's key and cannot rewrap it */ ret = zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin_name, sizeof (origin_name), NULL, NULL, 0, B_TRUE); if (ret == 0 && strcmp(origin_name, "") != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Keys cannot be changed on clones.")); ret = EINVAL; goto error; } /* * If the user wants to use the inheritkey variant of this function * we don't need to collect any crypto arguments. */ if (!inheritkey) { /* validate the provided properties */ ret = zfs_crypto_verify_rewrap_nvlist(zhp, raw_props, &props, errbuf); if (ret != 0) goto error; /* * Load keyformat and keylocation from the nvlist. Fetch from * the dataset properties if not specified. */ (void) nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat); (void) nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation); if (is_encroot) { /* * If this is already an ecryption root, just keep * any properties not set by the user. */ if (keyformat == ZFS_KEYFORMAT_NONE) { keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_KEYFORMAT), keyformat); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to " "get existing keyformat " "property.")); goto error; } } if (keylocation == NULL) { ret = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION, prop_keylocation, sizeof (prop_keylocation), NULL, NULL, 0, B_TRUE); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to " "get existing keylocation " "property.")); goto error; } keylocation = prop_keylocation; } } else { /* need a new key for non-encryption roots */ if (keyformat == ZFS_KEYFORMAT_NONE) { ret = EINVAL; zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Keyformat required " "for new encryption root.")); goto error; } /* default to prompt if no keylocation is specified */ if (keylocation == NULL) { keylocation = "prompt"; ret = nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_KEYLOCATION), keylocation); if (ret != 0) goto error; } } /* fetch the new wrapping key and associated properties */ ret = populate_create_encryption_params_nvlists(zhp->zfs_hdl, zhp, B_TRUE, keyformat, keylocation, props, &wkeydata, &wkeylen); if (ret != 0) goto error; } else { /* check that zhp is an encryption root */ if (!is_encroot) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Key inheritting can only be performed on " "encryption roots.")); ret = EINVAL; goto error; } /* get the parent's name */ ret = zfs_parent_name(zhp, parent_name, sizeof (parent_name)); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Root dataset cannot inherit key.")); ret = EINVAL; goto error; } /* get a handle to the parent */ pzhp = make_dataset_handle(zhp->zfs_hdl, parent_name); if (pzhp == NULL) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to lookup parent.")); ret = ENOENT; goto error; } /* parent must be encrypted */ pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); if (pcrypt == ZIO_CRYPT_OFF) { zfs_error_aux(pzhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Parent must be encrypted.")); ret = EINVAL; goto error; } /* check that the parent's key is loaded */ pkeystatus = zfs_prop_get_int(pzhp, ZFS_PROP_KEYSTATUS); if (pkeystatus == ZFS_KEYSTATUS_UNAVAILABLE) { zfs_error_aux(pzhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Parent key must be loaded.")); ret = EACCES; goto error; } } /* check that the key is loaded */ keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Key must be loaded.")); ret = EACCES; goto error; } /* call the ioctl */ ret = lzc_change_key(zhp->zfs_name, cmd, props, wkeydata, wkeylen); if (ret != 0) { switch (ret) { case EPERM: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Permission denied.")); break; case EINVAL: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Invalid properties for key change.")); break; case EACCES: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Key is not currently loaded.")); break; } zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); } if (pzhp != NULL) zfs_close(pzhp); if (props != NULL) nvlist_free(props); if (wkeydata != NULL) free(wkeydata); return (ret); error: if (pzhp != NULL) zfs_close(pzhp); if (props != NULL) nvlist_free(props); if (wkeydata != NULL) free(wkeydata); zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); return (ret); }
int zfs_key_load(zfs_handle_t *zhp, boolean_t mount, boolean_t share, boolean_t recursive) { zfs_handle_t *pzhp = NULL; zprop_source_t propsrctype; char source[ZFS_MAXNAMELEN]; char keysource[MAXNAMELEN]; uint64_t ret, crypt, keystatus; zfs_cmd_t zc = { {0 }}; char errbuf[1024]; fprintf(stderr, "zfs_key_load\r\n"); (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name)); (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot load key for '%s'"), zc.zc_name); zfs_refresh_properties(zhp); crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); if (crypt == ZIO_CRYPT_OFF) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "encryption not enabled on dataset %s."), zc.zc_name); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); if (keystatus == ZFS_CRYPT_KEY_AVAILABLE && !recursive) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "already loaded.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } if (zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, ZFS_MAXNAMELEN, &propsrctype, source, sizeof (source), FALSE) != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "no keysource property available.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } if (propsrctype == ZPROP_SRC_INHERITED) { #if 0 // FIXME if (strcmp(source, ZONE_INVISIBLE_SOURCE) == 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "key must be loaded from global zone.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } #endif pzhp = make_dataset_handle(zhp->zfs_hdl, source); if (pzhp == NULL) { errno = EINVAL; return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } keystatus = zfs_prop_get_int(pzhp, ZFS_PROP_KEYSTATUS); zfs_close(pzhp); } if (propsrctype == ZPROP_SRC_DEFAULT) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "invalid keysource property.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } if (!zfs_can_prompt_if_needed(keysource)) { errno = ENOTTY; return (-1); } /* * NONE we are the top ds asking for crypto so we * need to get and load the key. * * UNAVAILABLE we need to load the key of a higher level * dataset. * * AVAILABLE we are done other than filling in who we * are inheriting the wrapping key from. */ if (propsrctype == ZPROP_SRC_INHERITED && keystatus == ZFS_CRYPT_KEY_AVAILABLE) { (void) strlcpy(zc.zc_crypto.zic_inherit_dsname, source, sizeof (zc.zc_crypto.zic_inherit_dsname)); ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_INHERIT, &zc); goto out; } zc.zc_crypto.zic_crypt = crypt; ret = key_hdl_to_zc(zhp->zfs_hdl, zhp, keysource, crypt, &zc, ZFS_CRYPTO_KEY_LOAD); if (ret != 0) { if (errno == ENOTTY) ret = 0; goto out; } ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_LOAD, &zc); out: if (ret != 0) { if (errno == EACCES) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "incorrect key.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } else if (!recursive) { if (errno == EEXIST) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "already loaded.")); } else if (zhp->zfs_hdl->libzfs_desc_active == 0) { zfs_error_aux(zhp->zfs_hdl, strerror(errno)); } return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } } zfs_refresh_properties(zhp); if (mount) { if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { if (recursive) { ret = zfs_mountall(zhp, 0); } else { ret = zfs_mount(zhp, NULL, 0); } if (ret == 0 && share) { ret = zfs_share(zhp); } } } return (ret); }
/* * zfs_crypto_rename_check * * Can't rename "out" of same hierarchy if keysource would change. * * If this dataset isn't encrypted we allow the rename, unless it * is being placed "below" an encrypted one. */ int zfs_crypto_rename_check(zfs_handle_t *zhp, zfs_cmd_t *zc) { uint64_t crypt, pcrypt; zfs_handle_t *pzhp; zprop_source_t propsrctype, ppropsrctype; char keysource[ZFS_MAXNAMELEN]; char pkeysource[ZFS_MAXNAMELEN]; char propsrc[ZFS_MAXNAMELEN]; char psource[ZFS_MAXNAMELEN]; char oparent[ZFS_MAXNAMELEN]; char nparent[ZFS_MAXNAMELEN]; char errbuf[1024]; if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) return (0); (void) zfs_parent_name(zc->zc_name, oparent, sizeof (oparent)); (void) zfs_parent_name(zc->zc_value, nparent, sizeof (nparent)); /* Simple rename in place */ if (strcmp(oparent, nparent) == 0) { return (0); } (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zfs_get_name(zhp)); crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); /* parent should never be null */ pzhp = make_dataset_handle(zhp->zfs_hdl, nparent); if (pzhp == NULL) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "failed to obtain parent to check encryption property.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); /* If no crypt involved then we are done. */ if (crypt == ZIO_CRYPT_OFF && pcrypt == ZIO_CRYPT_OFF) { zfs_close(pzhp); return (0); } /* Just like create time no unencrypted below encrypted . */ if (crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) { zfs_close(pzhp); zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Can not move unencrypted dataset below " "encrypted datasets.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } /* * From here on we need to check that keysource is * from the same dataset if it is being inherited */ if (zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, ZFS_MAXNAMELEN, &propsrctype, propsrc, sizeof (propsrc), FALSE) != 0) { zfs_close(pzhp); zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "keysource must be provided.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } if (propsrctype == ZPROP_SRC_LOCAL) { zfs_close(pzhp); return (0); } if (zfs_prop_get(pzhp, ZFS_PROP_KEYSOURCE, pkeysource, ZFS_MAXNAMELEN, &ppropsrctype, psource, sizeof (psource), FALSE) != 0) { zfs_close(pzhp); zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "keysource must be provided.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } if (propsrctype == ZPROP_SRC_INHERITED && ((strcmp(propsrc, nparent) == 0) || (strcmp(propsrc, psource) == 0))) { zfs_close(pzhp); return (0); } zfs_close(pzhp); zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "keysource doesn't allow for rename, make keysource local.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); }
/* * zfs_crypto_zckey * * Called for creating new filesystems and clones and receiving. * * For encryption != off get the key material. */ int zfs_crypto_zckey(libzfs_handle_t *hdl, zfs_crypto_zckey_t cmd, nvlist_t *props, zfs_cmd_t *zc) { uint64_t crypt = ZIO_CRYPT_INHERIT, pcrypt = ZIO_CRYPT_DEFAULT; char *keysource = NULL; int ret = 0; int keystatus; zfs_handle_t *pzhp = NULL; boolean_t inherit_crypt = B_TRUE; boolean_t inherit_keysource = B_TRUE; boolean_t recv_existing = B_FALSE; boolean_t recv_clone = B_FALSE; boolean_t keysource_free = B_FALSE; zprop_source_t propsrctype = ZPROP_SRC_DEFAULT; char propsrc[ZFS_MAXNAMELEN]; char errbuf[1024]; char target[MAXNAMELEN]; char parent[MAXNAMELEN]; char *strval; zfs_cmd_target_dsname(zc, cmd, target, sizeof (target)); if (zfs_parent_name(target, parent, sizeof (parent)) != 0) parent[0] = '\0'; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot create '%s'"), target); if (props != NULL) { if (nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &strval) == 0) { (void) zfs_prop_string_to_index(ZFS_PROP_ENCRYPTION, strval, &crypt); inherit_crypt = B_FALSE; } else if (nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt) == 0) { inherit_crypt = B_FALSE; } else { inherit_crypt = B_TRUE; } if (nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource) == 0) { inherit_keysource = B_FALSE; } } if (cmd == ZFS_CRYPTO_CREATE) { pzhp = make_dataset_handle(hdl, parent); } else if (cmd == ZFS_CRYPTO_CLONE) { zfs_handle_t *szhp = make_dataset_handle(hdl, zc->zc_value); if (szhp == NULL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "parent not found")); (void) zfs_error(hdl, EZFS_NOENT, errbuf); ret = -1; goto out; } crypt = zfs_prop_get_int(szhp, ZFS_PROP_ENCRYPTION); zfs_close(szhp); pzhp = make_dataset_handle(hdl, parent); } else if (cmd == ZFS_CRYPTO_RECV) { if (zfs_dataset_exists(hdl, target, ZFS_TYPE_DATASET)) { pzhp = make_dataset_handle(hdl, target); pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); if (crypt != pcrypt && crypt != ZIO_CRYPT_INHERIT) { const char *stream_crypt_str = NULL; const char *pcrypt_str = NULL; (void) zfs_prop_index_to_string( ZFS_PROP_ENCRYPTION, pcrypt, &pcrypt_str); (void) zfs_prop_index_to_string( ZFS_PROP_ENCRYPTION, crypt, &stream_crypt_str); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "stream encryption '%s'(%llu) differs " "from receiving dataset value '%s'(%llu)"), stream_crypt_str, crypt, pcrypt_str, pcrypt); ret = -1; goto out; } inherit_crypt = B_TRUE; inherit_keysource = B_TRUE; recv_existing = B_TRUE; } else { if (strlen(zc->zc_string) != 0) { pzhp = make_dataset_handle(hdl, zc->zc_string); recv_clone = B_TRUE; } else { pzhp = make_dataset_handle(hdl, parent); } } } if (cmd != ZFS_CRYPTO_PCREATE) { if (pzhp == NULL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "parent not found")); (void) zfs_error(hdl, EZFS_NOENT, errbuf); ret = -1; goto out; } pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); } if (pcrypt != ZIO_CRYPT_OFF && crypt == ZIO_CRYPT_OFF) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " "encryption value. dataset must be encrypted.")); (void) zfs_error(hdl, EZFS_KEYERR, errbuf); ret = -1; goto out; } if (crypt == ZIO_CRYPT_INHERIT) { crypt = pcrypt; } /* * If we have nothing to do then bail out, but make one last check * that keysource wasn't specified when there is no crypto going on. */ if (crypt == ZIO_CRYPT_OFF && !inherit_keysource) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "keysource " "can not be specified when encryption is off.")); (void) zfs_error(hdl, EZFS_KEYERR, errbuf); ret = -1; goto out; } else if (crypt == ZIO_CRYPT_OFF) { ret = 0; goto out; } /* * Need to pass down the inherited crypt value so that * dsl_crypto_key_gen() can see the same that we saw. */ zc->zc_crypto.zic_crypt = crypt; zc->zc_crypto.zic_clone_newkey = hdl->libzfs_crypt.zc_clone_newkey; /* * Here we have encryption on so we need to find a valid keysource * property. * * Now lets see if we have an explicit setting for keysource and * we have validate it; otherwise, if we inherit then it is already * validated. */ if (!inherit_keysource) { if (!zfs_valid_keysource(keysource)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid keysource \"%s\""), keysource); (void) zfs_error(hdl, EZFS_KEYERR, errbuf); ret = -1; goto out; } /* * If keysource is local then encryption has to be as well * otherwise we could end up with the wrong sized keys. */ if (inherit_crypt) { VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt) == 0); VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_CHECKSUM), ZIO_CHECKSUM_SHA256_MAC) == 0); } } else { /* Get the already validated keysource from our parent */ keysource = zfs_alloc(hdl, ZFS_MAXNAMELEN); if (keysource == NULL) { ret = no_memory(hdl); goto out; } keysource_free = B_TRUE; if (pzhp != NULL && zfs_prop_get(pzhp, ZFS_PROP_KEYSOURCE, keysource, ZFS_MAXNAMELEN, &propsrctype, propsrc, sizeof (propsrc), FALSE) != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "keysource must be provided.")); (void) zfs_error(hdl, EZFS_KEYERR, errbuf); ret = -1; goto out; } if (recv_existing) { (void) strlcpy(propsrc, target, sizeof (propsrc)); } else if (recv_clone) { (void) strlcpy(propsrc, zc->zc_string, sizeof (propsrc)); } else if (propsrctype == ZPROP_SRC_LOCAL || propsrctype == ZPROP_SRC_RECEIVED) { (void) strlcpy(propsrc, parent, sizeof (propsrc)); } else if (propsrctype == ZPROP_SRC_DEFAULT && pcrypt == ZIO_CRYPT_OFF) { /* * "Default" to "passphrase,prompt". The obvious * thing to do would be to set this in zfs_prop.c * as the property default. However that doesn't * work here because we don't want keysource set * for datasets that have encryption=off. If we * ever change the default to encryption=on then * the default of keysource can change too. * This is needed because of how inheritance happens * with defaulted properties, they show up as * "default" not "inherit" but we need "inherit" * to find the wrapping key if we are actually * inheriting keysource. */ inherit_keysource = B_FALSE; if (props == NULL) { VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0)); } (void) strlcpy(keysource, "passphrase,prompt", ZFS_MAXNAMELEN); VERIFY(nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), keysource) == 0); VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt) == 0); VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_CHECKSUM), ZIO_CHECKSUM_SHA256_MAC) == 0); goto load_key; } else if (propsrctype == ZPROP_SRC_DEFAULT && pcrypt != ZIO_CRYPT_OFF) { abort(); #if 0 // FIXME } else if (strcmp(propsrc, ZONE_INVISIBLE_SOURCE) == 0) { /* * Assume key is available and handle failure ioctl * ENOKEY errors later. */ zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_INHERIT; (void) strlcpy(zc->zc_crypto.zic_inherit_dsname, propsrc, sizeof (zc->zc_crypto.zic_inherit_dsname)); ret = 0; goto out; #endif } else if (propsrctype != ZPROP_SRC_DEFAULT) { if (pzhp != NULL) zfs_close(pzhp); VERIFY((pzhp = make_dataset_handle(hdl, propsrc)) != 0); } keystatus = zfs_prop_get_int(pzhp, ZFS_PROP_KEYSTATUS); /* * AVAILABLE we are done other than filling in who we * are inheriting the wrapping key from. * * UNAVAILABLE we need to load the key of a higher level * dataset. */ if (keystatus == ZFS_CRYPT_KEY_AVAILABLE) { zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_INHERIT; (void) strlcpy(zc->zc_crypto.zic_inherit_dsname, propsrc, sizeof (zc->zc_crypto.zic_inherit_dsname)); ret = 0; goto out; } else if (keystatus == ZFS_CRYPT_KEY_UNAVAILABLE) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "zfs key -l %s required."), parent); (void) zfs_error(hdl, EZFS_KEYERR, errbuf); ret = -1; goto out; } } load_key: if (!zfs_can_prompt_if_needed(keysource)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "unable to prompt for key material keysource = \"%s\"\n"), keysource); errno = ENOTTY; return (-1); } ret = key_hdl_to_zc(hdl, NULL, keysource, crypt, zc, cmd); if (ret != 0) { ret = -1; (void) zfs_error(hdl, EZFS_KEYERR, errbuf); goto out; } zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_LOAD; ret = 0; out: if (pzhp) zfs_close(pzhp); if (keysource_free) free(keysource); return (ret); }
/* * Unshare and unmount all datasets within the given pool. We don't want to * rely on traversing the DSL to discover the filesystems within the pool, * because this may be expensive (if not all of them are mounted), and can fail * arbitrarily (on I/O error, for example). Instead, we walk /etc/mtab and * gather all the filesystems that are currently mounted. */ int zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) { int used, alloc; struct mnttab entry; size_t namelen; char **mountpoints = NULL; zfs_handle_t **datasets = NULL; libzfs_handle_t *hdl = zhp->zpool_hdl; int i; int ret = -1; int flags = (force ? MS_FORCE : 0); namelen = strlen(zhp->zpool_name); /* Reopen MNTTAB to prevent reading stale data from open file */ if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL) return (ENOENT); used = alloc = 0; while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { /* * Ignore filesystems not within this pool. */ if (entry.mnt_fstype == NULL || strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || (entry.mnt_special[namelen] != '/' && #ifdef __APPLE__ /* * On OS X, '@' is possible too since we're temporarily * allowing manual snapshot mounting. */ entry.mnt_special[namelen] != '@' && #endif /* __APPLE__ */ entry.mnt_special[namelen] != '\0')) continue; /* * At this point we've found a filesystem within our pool. Add * it to our growing list. */ if (used == alloc) { if (alloc == 0) { if ((mountpoints = zfs_alloc(hdl, 8 * sizeof (void *))) == NULL) goto out; if ((datasets = zfs_alloc(hdl, 8 * sizeof (void *))) == NULL) goto out; alloc = 8; } else { void *ptr; if ((ptr = zfs_realloc(hdl, mountpoints, alloc * sizeof (void *), alloc * 2 * sizeof (void *))) == NULL) goto out; mountpoints = ptr; if ((ptr = zfs_realloc(hdl, datasets, alloc * sizeof (void *), alloc * 2 * sizeof (void *))) == NULL) goto out; datasets = ptr; alloc *= 2; } } if ((mountpoints[used] = zfs_strdup(hdl, entry.mnt_mountp)) == NULL) goto out; /* * This is allowed to fail, in case there is some I/O error. It * is only used to determine if we need to remove the underlying * mountpoint, so failure is not fatal. */ datasets[used] = make_dataset_handle(hdl, entry.mnt_special); used++; } /* * At this point, we have the entire list of filesystems, so sort it by * mountpoint. */ qsort(mountpoints, used, sizeof (char *), mountpoint_compare); /* * Walk through and first unshare everything. */ for (i = 0; i < used; i++) { zfs_share_proto_t *curr_proto; for (curr_proto = share_all_proto; *curr_proto != PROTO_END; curr_proto++) { if (is_shared(hdl, mountpoints[i], *curr_proto) && unshare_one(hdl, mountpoints[i], mountpoints[i], *curr_proto) != 0) goto out; } } /* * Now unmount everything, removing the underlying directories as * appropriate. */ for (i = 0; i < used; i++) { if (unmount_one(hdl, mountpoints[i], flags) != 0) goto out; } for (i = 0; i < used; i++) { if (datasets[i]) remove_mountpoint(datasets[i]); } // Surely there exists a better way to iterate a POOL to find its ZVOLs? zfs_iter_root(hdl, zpool_disable_volumes, (void *) zpool_get_name(zhp)); ret = 0; out: for (i = 0; i < used; i++) { if (datasets[i]) zfs_close(datasets[i]); free(mountpoints[i]); } free(datasets); free(mountpoints); return (ret); }