int zfs_key_unload(zfs_handle_t *zhp, boolean_t force) { zfs_cmd_t zc = { { 0 }}; int ret = 0; int terrno; int type = zfs_get_type(zhp); char errbuf[1024]; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot unload key for '%s'"), zfs_get_name(zhp)); if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) == ZIO_CRYPT_OFF) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "no key to unload when encryption=off.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } if (zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) != ZFS_CRYPT_KEY_AVAILABLE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "key not present.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } /* * We need to be sure that all the data has been written to * disk before we unload the key so we first have to attempt * an unmount, if that fails we don't continue with the key unload * and instead return the error from zfs_umount. */ if (type == ZFS_TYPE_FILESYSTEM) { if (zfs_is_mounted(zhp, NULL)) { ret = zfs_unmountall(zhp, force ? MS_FORCE : 0); if (ret) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "failed to unload key: unmount failed")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } } } (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name)); errno = 0; ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_UNLOAD, &zc); terrno = errno; if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "failed to unload key: %s"), strerror(terrno)); errno = terrno; /* make sure it is the zfs_ioctl errno */ return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); } zfs_refresh_properties(zhp); return (0); }
/* * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or * reshare the filesystems as necessary. In changelist_gather() we recorded * whether the filesystem was previously shared or mounted. The action we take * depends on the previous state, and whether the value was previously 'legacy'. * For non-legacy properties, we only remount/reshare the filesystem if it was * previously mounted/shared. Otherwise, we always remount/reshare the * filesystem. */ int changelist_postfix(prop_changelist_t *clp) { prop_changenode_t *cn; char shareopts[ZFS_MAXPROPLEN]; int errors = 0; libzfs_handle_t *hdl; /* * If we're changing the mountpoint, attempt to destroy the underlying * mountpoint. All other datasets will have inherited from this dataset * (in which case their mountpoints exist in the filesystem in the new * location), or have explicit mountpoints set (in which case they won't * be in the changelist). */ if ((cn = uu_list_last(clp->cl_list)) == NULL) return (0); if (clp->cl_prop == ZFS_PROP_MOUNTPOINT && !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) { remove_mountpoint(cn->cn_handle); } /* * It is possible that the changelist_prefix() used libshare * to unshare some entries. Since libshare caches data, an * attempt to reshare during postfix can fail unless libshare * is uninitialized here so that it will reinitialize later. */ if (cn->cn_handle != NULL) { hdl = cn->cn_handle->zfs_hdl; assert(hdl != NULL); zfs_uninit_libshare(hdl); } /* * We walk the datasets in reverse, because we want to mount any parent * datasets before mounting the children. We walk all datasets even if * there are errors. */ for (cn = uu_list_last(clp->cl_list); cn != NULL; cn = uu_list_prev(clp->cl_list, cn)) { boolean_t sharenfs; boolean_t sharesmb; boolean_t mounted; /* * If we are in the global zone, but this dataset is exported * to a local zone, do nothing. */ if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) continue; /* Only do post-processing if it's required */ if (!cn->cn_needpost) continue; cn->cn_needpost = B_FALSE; zfs_refresh_properties(cn->cn_handle); if (ZFS_IS_VOLUME(cn->cn_handle)) continue; /* * Remount if previously mounted or mountpoint was legacy, * or sharenfs or sharesmb property is set. */ sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB, shareopts, sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) || zfs_is_mounted(cn->cn_handle, NULL); if (!mounted && (cn->cn_mounted || ((sharenfs || sharesmb || clp->cl_waslegacy) && (zfs_prop_get_int(cn->cn_handle, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) { if (zfs_mount(cn->cn_handle, NULL, 0) != 0) errors++; else mounted = TRUE; } /* * If the file system is mounted we always re-share even * if the filesystem is currently shared, so that we can * adopt any new options. */ if (sharenfs && mounted) errors += zfs_share_nfs(cn->cn_handle); else if (cn->cn_shared || clp->cl_waslegacy) errors += zfs_unshare_nfs(cn->cn_handle, NULL); if (sharesmb && mounted) errors += zfs_share_smb(cn->cn_handle); else if (cn->cn_shared || clp->cl_waslegacy) errors += zfs_unshare_smb(cn->cn_handle, NULL); } return (errors ? -1 : 0); }
/* * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or * reshare the filesystems as necessary. In changelist_gather() we recorded * whether the filesystem was previously shared or mounted. The action we take * depends on the previous state, and whether the value was previously 'legacy'. * For non-legacy properties, we only remount/reshare the filesystem if it was * previously mounted/shared. Otherwise, we always remount/reshare the * filesystem. */ int changelist_postfix(prop_changelist_t *clp) { prop_changenode_t *cn; char shareopts[ZFS_MAXPROPLEN]; int ret = 0; libzfs_handle_t *hdl; /* * If we're changing the mountpoint, attempt to destroy the underlying * mountpoint. All other datasets will have inherited from this dataset * (in which case their mountpoints exist in the filesystem in the new * location), or have explicit mountpoints set (in which case they won't * be in the changelist). */ if ((cn = uu_list_last(clp->cl_list)) == NULL) return (0); if (clp->cl_prop == ZFS_PROP_MOUNTPOINT) remove_mountpoint(cn->cn_handle); /* * It is possible that the changelist_prefix() used libshare * to unshare some entries. Since libshare caches data, an * attempt to reshare during postfix can fail unless libshare * is uninitialized here so that it will reinitialize later. */ if (cn->cn_handle != NULL) { hdl = cn->cn_handle->zfs_hdl; assert(hdl != NULL); #ifndef __APPLE__ zfs_uninit_libshare(hdl); #endif } /* * We walk the datasets in reverse, because we want to mount any parent * datasets before mounting the children. */ for (cn = uu_list_last(clp->cl_list); cn != NULL; cn = uu_list_prev(clp->cl_list, cn)) { boolean_t sharenfs; #ifndef __APPLE__ /* * If we are in the global zone, but this dataset is exported * to a local zone, do nothing. */ if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) continue; #endif /*!__APPLE__*/ zfs_refresh_properties(cn->cn_handle); if (ZFS_IS_VOLUME(cn->cn_handle)) { /* * If we're doing a rename, recreate the /dev/zvol * links. */ if (clp->cl_realprop == ZFS_PROP_NAME && zvol_create_link(cn->cn_handle->zfs_hdl, cn->cn_handle->zfs_name) != 0) { ret = -1; } else if (cn->cn_shared || clp->cl_prop == ZFS_PROP_SHAREISCSI) { if (zfs_prop_get(cn->cn_handle, ZFS_PROP_SHAREISCSI, shareopts, sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0 && strcmp(shareopts, "off") == 0) { ret = zfs_unshare_iscsi(cn->cn_handle); } else { ret = zfs_share_iscsi(cn->cn_handle); } } continue; } /* * Remount if previously mounted or mountpoint was legacy, * or sharenfs property is set. */ sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs) && !zfs_is_mounted(cn->cn_handle, NULL) && zfs_mount(cn->cn_handle, NULL, 0) != 0) ret = -1; /* * We always re-share even if the filesystem is currently * shared, so that we can adopt any new options. */ if (cn->cn_shared || clp->cl_waslegacy || sharenfs) { if (sharenfs) ret = zfs_share_nfs(cn->cn_handle); else ret = zfs_unshare_nfs(cn->cn_handle, NULL); } } return (ret); }
/*ARGSUSED*/ static int zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) { char *osname; pathname_t spn; int error = 0; uio_seg_t fromspace = (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE; int canwrite; if (mvp->v_type != VDIR) return (ENOTDIR); mutex_enter(&mvp->v_lock); if ((uap->flags & MS_REMOUNT) == 0 && (uap->flags & MS_OVERLAY) == 0 && (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { mutex_exit(&mvp->v_lock); return (EBUSY); } mutex_exit(&mvp->v_lock); /* * ZFS does not support passing unparsed data in via MS_DATA. * Users should use the MS_OPTIONSTR interface; this means * that all option parsing is already done and the options struct * can be interrogated. */ if ((uap->flags & MS_DATA) && uap->datalen > 0) return (EINVAL); /* * When doing a remount, we simply refresh our temporary properties * according to those options set in the current VFS options. */ if (uap->flags & MS_REMOUNT) { return (zfs_refresh_properties(vfsp)); } /* * Get the objset name (the "special" mount argument). */ if (error = pn_get(uap->spec, fromspace, &spn)) return (error); osname = spn.pn_path; if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) goto out; /* * Refuse to mount a filesystem if we are in a local zone and the * dataset is not visible. */ if (!INGLOBALZONE(curproc) && (!zone_dataset_visible(osname, &canwrite) || !canwrite)) { error = EPERM; goto out; } error = zfs_domount(vfsp, osname, cr); out: pn_free(&spn); return (error); }
static int zfs_mountroot(vfs_t *vfsp, enum whymountroot why) { int error = 0; int ret = 0; static int zfsrootdone = 0; zfsvfs_t *zfsvfs = NULL; znode_t *zp = NULL; vnode_t *vp = NULL; ASSERT(vfsp); /* * The filesystem that we mount as root is defined in * /etc/system using the zfsroot variable. The value defined * there is copied early in startup code to zfs_bootpath * (defined in modsysfile.c). */ if (why == ROOT_INIT) { if (zfsrootdone++) return (EBUSY); /* * This needs to be done here, so that when we return from * mountroot, the vfs resource name will be set correctly. */ if (snprintf(rootfs.bo_name, BO_MAXOBJNAME, "%s", zfs_bootpath) >= BO_MAXOBJNAME) return (ENAMETOOLONG); if (error = vfs_lock(vfsp)) return (error); if (error = zfs_domount(vfsp, zfs_bootpath, CRED())) goto out; zfsvfs = (zfsvfs_t *)vfsp->vfs_data; ASSERT(zfsvfs); if (error = zfs_zget(zfsvfs, zfsvfs->z_root, &zp)) goto out; vp = ZTOV(zp); mutex_enter(&vp->v_lock); vp->v_flag |= VROOT; mutex_exit(&vp->v_lock); rootvp = vp; /* * The zfs_zget call above returns with a hold on vp, we release * it here. */ VN_RELE(vp); /* * Mount root as readonly initially, it will be remouted * read/write by /lib/svc/method/fs-usr. */ readonly_changed_cb(vfsp->vfs_data, B_TRUE); vfs_add((struct vnode *)0, vfsp, (vfsp->vfs_flag & VFS_RDONLY) ? MS_RDONLY : 0); out: vfs_unlock(vfsp); ret = (error) ? error : 0; return (ret); } else if (why == ROOT_REMOUNT) { readonly_changed_cb(vfsp->vfs_data, B_FALSE); vfsp->vfs_flag |= VFS_REMOUNT; return (zfs_refresh_properties(vfsp)); } else if (why == ROOT_UNMOUNT) { zfs_unregister_callbacks((zfsvfs_t *)vfsp->vfs_data); (void) zfs_sync(vfsp, 0, 0); return (0); } /* * if "why" is equal to anything else other than ROOT_INIT, * ROOT_REMOUNT, or ROOT_UNMOUNT, we do not support it. */ return (ENOTSUP); }
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); }
int zfs_key_change(zfs_handle_t *zhp, boolean_t recursing, nvlist_t *props) { char errbuf[1024]; int ret; zfs_cmd_t zc = { { 0 } }; char keysource[ZFS_MAXNAMELEN]; uint64_t crypt; zprop_source_t propsrctype = ZPROP_SRC_NONE; char propsrc[ZFS_MAXNAMELEN] = { 0 }; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot change wrapping key for '%s'"), zfs_get_name(zhp)); crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); if (crypt == ZIO_CRYPT_OFF) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "cannot change key when encryption=off")); goto error; } switch (zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS)) { case ZFS_CRYPT_KEY_NONE: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "inconsistent state encryption enabled but " "key not defined.")); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); case ZFS_CRYPT_KEY_UNAVAILABLE: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "load existing key first: 'zfs key -l <dataset>'.")); goto error; } (void) zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, ZFS_MAXNAMELEN, &propsrctype, propsrc, ZFS_MAXNAMELEN, B_TRUE); if (!(propsrctype & ZPROP_SRC_LOCAL || propsrctype & ZPROP_SRC_RECEIVED)) { if (recursing) return (0); zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "keysource property not local, change key on '%s'."), propsrc); goto error; } zhp->zfs_hdl->libzfs_crypt.zc_is_key_change = B_TRUE; /* * The only thing we currently expect in props is a keysource * if we have props without keysource then that isn't valid. */ if (props != NULL) { char *nkeysource; ret = nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), (char **)&nkeysource); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "invalid props for key change; expected " "%s property missing."), zfs_prop_to_name(ZFS_PROP_KEYSOURCE)); goto error; } (void) strlcpy(keysource, nkeysource, sizeof (keysource)); } (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name)); zc.zc_crypto.zic_crypt = crypt; if (!zfs_can_prompt_if_needed(keysource)) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "unable to prompt for new wrapping key.")); errno = ENOTTY; goto error; } ret = key_hdl_to_zc(zhp->zfs_hdl, zhp, keysource, crypt, &zc, ZFS_CRYPTO_KEY_CHANGE); if (props != NULL) { if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, props) != 0) goto error; } if (ret == 0) { /* Send change to kernel */ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_CHANGE, &zc); zcmd_free_nvlists(&zc); if (ret != 0) { return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf)); } zfs_refresh_properties(zhp); return (ret); } error: zcmd_free_nvlists(&zc); return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf)); }