/* * Mount the given filesystem. */ int zfs_mount(zfs_handle_t *zhp, const char *options, int flags) { struct stat buf; char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; char overlay[ZFS_MAXPROPLEN]; libzfs_handle_t *hdl = zhp->zfs_hdl; int remount = 0, rc; if (options == NULL) { (void) strlcpy(mntopts, MNTOPT_DEFAULTS, sizeof (mntopts)); } else { (void) strlcpy(mntopts, options, sizeof (mntopts)); } if (strstr(mntopts, MNTOPT_REMOUNT) != NULL) remount = 1; /* * If the pool is imported read-only then all mounts must be read-only */ if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts)); if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); /* * Append default mount options which apply to the mount point. * This is done because under Linux (unlike Solaris) multiple mount * points may reference a single super block. This means that just * given a super block there is no back reference to update the per * mount point options. */ rc = zfs_add_options(zhp, mntopts, sizeof (mntopts)); if (rc) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "default options unavailable")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } /* * Append zfsutil option so the mount helper allow the mount */ strlcat(mntopts, "," MNTOPT_ZFSUTIL, sizeof (mntopts)); /* Create the directory if it doesn't already exist */ if (lstat(mountpoint, &buf) != 0) { if (mkdirp(mountpoint, 0755) != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to create mountpoint")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } } /* * Overlay mounts are disabled by default but may be enabled * via the 'overlay' property or the 'zfs mount -O' option. */ if (!(flags & MS_OVERLAY)) { if (zfs_prop_get(zhp, ZFS_PROP_OVERLAY, overlay, sizeof (overlay), NULL, NULL, 0, B_FALSE) == 0) { if (strcmp(overlay, "on") == 0) { flags |= MS_OVERLAY; } } } /* * Determine if the mountpoint is empty. If so, refuse to perform the * mount. We don't perform this check if 'remount' is * specified or if overlay option(-O) is given */ if ((flags & MS_OVERLAY) == 0 && !remount && !dir_is_empty(mountpoint)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "directory is not empty")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } /* perform the mount */ rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts); if (rc) { /* * Generic errors are nasty, but there are just way too many * from mount(), and they're well-understood. We pick a few * common ones to improve upon. */ if (rc == EBUSY) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "mountpoint or dataset is busy")); } else if (rc == EPERM) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Insufficient privileges")); } else if (rc == ENOTSUP) { char buf[256]; int spa_version; VERIFY(zfs_spa_version(zhp, &spa_version) == 0); (void) snprintf(buf, sizeof (buf), dgettext(TEXT_DOMAIN, "Can't mount a version %lld " "file system on a version %d pool. Pool must be" " upgraded to mount this file system."), (u_longlong_t)zfs_prop_get_int(zhp, ZFS_PROP_VERSION), spa_version); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf)); } else { zfs_error_aux(hdl, strerror(rc)); } return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), zhp->zfs_name)); } /* remove the mounted entry before re-adding on remount */ if (remount) libzfs_mnttab_remove(hdl, zhp->zfs_name); /* add the mounted entry into our cache */ libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, mntopts); return (0); }
/* * A ZFS file system iterator call-back function which returns the * zfs_handle_t for a ZFS file system on the specified mount point. */ static int match_mountpoint(zfs_handle_t *zhp, void *data) { int res; zfs_mount_data_t *cbp; char mp[ZFS_MAXPROPLEN]; if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { zfs_close(zhp); return (0); } /* First check if the dataset is mounted. */ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL, 0, B_FALSE) != 0 || strcmp(mp, "no") == 0) { zfs_close(zhp); return (0); } /* Now check mount point. */ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL, 0, B_FALSE) != 0) { zfs_close(zhp); return (0); } cbp = (zfs_mount_data_t *)data; if (strcmp(mp, "legacy") == 0) { /* If legacy, must look in mnttab for mountpoint. */ FILE *fp; struct mnttab entry; const char *nm; nm = zfs_get_name(zhp); if ((fp = fopen(MNTTAB, "r")) == NULL) { zfs_close(zhp); return (0); } while (getmntent(fp, &entry) == 0) { if (strcmp(nm, entry.mnt_special) == 0) { if (strcmp(entry.mnt_mountp, cbp->match_name) == 0) { (void) fclose(fp); cbp->match_handle = zhp; return (1); } break; } } (void) fclose(fp); } else if (strcmp(mp, cbp->match_name) == 0) { cbp->match_handle = zhp; return (1); } /* Iterate over any nested datasets. */ res = zfs_iter_filesystems(zhp, match_mountpoint, data); zfs_close(zhp); return (res); }
/* * Sort datasets by specified columns. * * o Numeric types sort in ascending order. * o String types sort in alphabetical order. * o Types inappropriate for a row sort that row to the literal * bottom, regardless of the specified ordering. * * If no sort columns are specified, or two datasets compare equally * across all specified columns, they are sorted alphabetically by name * with snapshots grouped under their parents. */ static int zfs_sort(const void *larg, const void *rarg, void *data) { zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; zfs_sort_column_t *sc = (zfs_sort_column_t *)data; zfs_sort_column_t *psc; for (psc = sc; psc != NULL; psc = psc->sc_next) { char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; char *lstr, *rstr; uint64_t lnum, rnum; boolean_t lvalid, rvalid; int ret = 0; /* * We group the checks below the generic code. If 'lstr' and * 'rstr' are non-NULL, then we do a string based comparison. * Otherwise, we compare 'lnum' and 'rnum'. */ lstr = rstr = NULL; if (psc->sc_prop == ZPROP_INVAL) { nvlist_t *luser, *ruser; nvlist_t *lval, *rval; luser = zfs_get_user_props(l); ruser = zfs_get_user_props(r); lvalid = (nvlist_lookup_nvlist(luser, psc->sc_user_prop, &lval) == 0); rvalid = (nvlist_lookup_nvlist(ruser, psc->sc_user_prop, &rval) == 0); if (lvalid) verify(nvlist_lookup_string(lval, ZPROP_VALUE, &lstr) == 0); if (rvalid) verify(nvlist_lookup_string(rval, ZPROP_VALUE, &rstr) == 0); } else if (psc->sc_prop == ZFS_PROP_NAME) { lvalid = rvalid = B_TRUE; (void) strlcpy(lbuf, zfs_get_name(l), sizeof (lbuf)); (void) strlcpy(rbuf, zfs_get_name(r), sizeof (rbuf)); lstr = lbuf; rstr = rbuf; } else if (zfs_prop_is_string(psc->sc_prop)) { lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); lstr = lbuf; rstr = rbuf; } else { lvalid = zfs_prop_valid_for_type(psc->sc_prop, zfs_get_type(l), B_FALSE); rvalid = zfs_prop_valid_for_type(psc->sc_prop, zfs_get_type(r), B_FALSE); if (lvalid) (void) zfs_prop_get_numeric(l, psc->sc_prop, &lnum, NULL, NULL, 0); if (rvalid) (void) zfs_prop_get_numeric(r, psc->sc_prop, &rnum, NULL, NULL, 0); } if (!lvalid && !rvalid) continue; else if (!lvalid) return (1); else if (!rvalid) return (-1); if (lstr) ret = strcmp(lstr, rstr); else if (lnum < rnum) ret = -1; else if (lnum > rnum) ret = 1; if (ret != 0) { if (psc->sc_reverse == B_TRUE) ret = (ret < 0) ? 1 : -1; return (ret); } } return (zfs_compare(larg, rarg, NULL)); }
/* * Mount the given filesystem. */ int zfs_mount(zfs_handle_t *zhp, const char *options, int flags) { struct stat buf; char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; libzfs_handle_t *hdl = zhp->zfs_hdl; if (options == NULL) mntopts[0] = '\0'; else (void) strlcpy(mntopts, options, sizeof (mntopts)); /* * If the pool is imported read-only then all mounts must be read-only */ if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) flags |= MS_RDONLY; if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); /* Create the directory if it doesn't already exist */ if (lstat(mountpoint, &buf) != 0) { if (mkdirp(mountpoint, 0755) != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to create mountpoint")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } } /* * Determine if the mountpoint is empty. If so, refuse to perform the * mount. We don't perform this check if MS_OVERLAY is specified, which * would defeat the point. We also avoid this check if 'remount' is * specified. */ if ((flags & MS_OVERLAY) == 0 && strstr(mntopts, MNTOPT_REMOUNT) == NULL && !dir_is_empty(mountpoint)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "directory is not empty")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } /* perform the mount */ if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { /* * Generic errors are nasty, but there are just way too many * from mount(), and they're well-understood. We pick a few * common ones to improve upon. */ if (errno == EBUSY) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "mountpoint or dataset is busy")); } else if (errno == EPERM) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Insufficient privileges")); } else if (errno == ENOTSUP) { char buf[256]; int spa_version; VERIFY(zfs_spa_version(zhp, &spa_version) == 0); (void) snprintf(buf, sizeof (buf), dgettext(TEXT_DOMAIN, "Can't mount a version %lld " "file system on a version %d pool. Pool must be" " upgraded to mount this file system."), (u_longlong_t)zfs_prop_get_int(zhp, ZFS_PROP_VERSION), spa_version); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf)); } else { zfs_error_aux(hdl, strerror(errno)); } return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), zhp->zfs_name)); } /* add the mounted entry into our cache */ libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, mntopts); return (0); }
/* * Mount the given filesystem. * * 'flags' appears pretty much always 0 here. */ int zfs_mount(zfs_handle_t *zhp, const char *options, int flags) { struct stat buf; char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; libzfs_handle_t *hdl = zhp->zfs_hdl; int remount; if (options == NULL) { mntopts[0] = '\0'; } else { (void) strlcpy(mntopts, options, sizeof (mntopts)); } if (strstr(mntopts, MNTOPT_REMOUNT) != NULL) remount = 1; /* * If the pool is imported read-only then all mounts must be read-only */ #ifdef __LINUX__ if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts)); #else if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) flags |= MS_RDONLY; #endif /* __LINUX__ */ if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); #ifdef __LINUX__ /* * Append default mount options which apply to the mount point. * This is done because under Linux (unlike Solaris) multiple mount * points may reference a single super block. This means that just * given a super block there is no back reference to update the per * mount point options. */ rc = zfs_add_options(zhp, &flags); if (rc) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "default options unavailable")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } /* * Append zfsutil option so the mount helper allow the mount */ strlcat(mntopts, "," MNTOPT_ZFSUTIL, sizeof (mntopts)); #endif /* __LINUX__ */ /* Create the directory if it doesn't already exist */ #ifdef __APPLE__ if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT && lstat(mountpoint, &buf) != 0) { #else if (lstat(mountpoint, &buf) != 0) { #endif if (mkdirp(mountpoint, 0755) != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to create mountpoint")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } } /* * Determine if the mountpoint is empty. If so, refuse to perform the * mount. We don't perform this check if 'remount' is * specified or if overlay option(-O) is given */ if ((flags & MS_OVERLAY) == 0 && !remount && !dir_is_empty(mountpoint)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "directory is not empty")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } /* perform the mount */ #ifdef __LINUX__ rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts); #elif defined(__APPLE__) || defined (__FREEBSD__) if (zmount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { #elif defined(__illumos__) if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { #endif /* __LINUX__*/ /* * Generic errors are nasty, but there are just way too many * from mount(), and they're well-understood. We pick a few * common ones to improve upon. */ if (errno == EBUSY) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "mountpoint or dataset is busy")); } else if (errno == EPERM) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Insufficient privileges")); } else if (errno == ENOTSUP) { char buf[256]; int spa_version; VERIFY(zfs_spa_version(zhp, &spa_version) == 0); (void) snprintf(buf, sizeof (buf), dgettext(TEXT_DOMAIN, "Can't mount a version %lld " "file system on a version %d pool. Pool must be" " upgraded to mount this file system."), (u_longlong_t)zfs_prop_get_int(zhp, ZFS_PROP_VERSION), spa_version); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf)); #ifdef __APPLE__ } else if (((errno == ESRCH) || (errno == EINVAL) || (errno == ENOENT && lstat(mountpoint, &buf) != 0)) && zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "The parent file system must be mounted first.")); #endif } else { zfs_error_aux(hdl, strerror(errno)); } return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), zhp->zfs_name)); } #ifdef __APPLE__ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) fprintf(stderr, "ZFS: snapshot mountpoint '%s'\n", mountpoint); if (!(flags & MS_RDONLY)) zfs_mount_seticon(mountpoint); #endif /* remove the mounted entry before re-adding on remount */ if (remount) libzfs_mnttab_remove(hdl, zhp->zfs_name); /* add the mounted entry into our cache */ libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, mntopts); return (0); } /* * Unmount a single filesystem. */ static int unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) { int error; #if 0 error = unmount(mountpoint, flags); if (unmount(mountpoint, flags) != 0) { return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), mountpoint)); } #else error = do_unmount(mountpoint, flags); if (error != 0) { return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), mountpoint)); } #endif return (0); } /* * Unmount the given filesystem. */ int zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) { libzfs_handle_t *hdl = zhp->zfs_hdl; #ifdef __LINUX__ struct mnttab search = { 0 }, entry; #else struct mnttab entry; #endif /* __LINUX__ */ char *mntpt = NULL; /* check to see if need to unmount the filesystem */ if (mountpoint != NULL || (((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) || (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)) && libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)) { /* * mountpoint may have come from a call to * getmnt/getmntany if it isn't NULL. If it is NULL, * we know it comes from getmntany which can then get * overwritten later. We strdup it to play it safe. */ if (mountpoint == NULL) mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); else mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint); /* * Unshare and unmount the filesystem */ #ifdef __illumos__ if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0) #else if (zfs_unshare_nfs(zhp, mntpt) != 0) #endif return (-1); if (unmount_one(hdl, mntpt, flags) != 0) { free(mntpt); #ifdef __illumos__ (void) zfs_shareall(zhp); #else (void) zfs_share_nfs(zhp); #endif return (-1); } libzfs_mnttab_remove(hdl, zhp->zfs_name); free(mntpt); } return (0); }
/* * 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 sa_get_zfs_shares(sa_handle_t handle, char *groupname) { sa_group_t zfsgroup; boolean_t nfs; boolean_t nfs_inherited; boolean_t smb; boolean_t smb_inherited; zfs_handle_t **zlist; char nfsshareopts[ZFS_MAXPROPLEN]; char smbshareopts[ZFS_MAXPROPLEN]; sa_share_t share; zprop_source_t source; char nfssourcestr[ZFS_MAXPROPLEN]; char smbsourcestr[ZFS_MAXPROPLEN]; char mountpoint[ZFS_MAXPROPLEN]; size_t count = 0, i; libzfs_handle_t *zfs_libhandle; int err = SA_OK; /* * If we can't access libzfs, don't bother doing anything. */ zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle; if (zfs_libhandle == NULL) return (SA_SYSTEM_ERR); zfsgroup = find_or_create_group(handle, groupname, NULL, &err); /* Not an error, this could be a legacy condition */ if (zfsgroup == NULL) return (SA_OK); /* * need to walk the mounted ZFS pools and datasets to * find shares that are possible. */ get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count); qsort(zlist, count, sizeof (void *), mountpoint_compare); for (i = 0; i < count; i++) { char *dataset; source = ZPROP_SRC_ALL; /* If no mountpoint, skip. */ if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) continue; /* * zfs_get_name value must not be freed. It is just a * pointer to a value in the handle. */ if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL) continue; /* * only deal with "mounted" file systems since * unmounted file systems can't actually be shared. */ if (!zfs_is_mounted(zlist[i], NULL)) continue; nfs = nfs_inherited = B_FALSE; if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, nfsshareopts, sizeof (nfsshareopts), &source, nfssourcestr, ZFS_MAXPROPLEN, B_FALSE) == 0 && strcmp(nfsshareopts, "off") != 0) { if (source & ZPROP_SRC_INHERITED) nfs_inherited = B_TRUE; else nfs = B_TRUE; } smb = smb_inherited = B_FALSE; if (zfs_prop_get(zlist[i], ZFS_PROP_SHARESMB, smbshareopts, sizeof (smbshareopts), &source, smbsourcestr, ZFS_MAXPROPLEN, B_FALSE) == 0 && strcmp(smbshareopts, "off") != 0) { if (source & ZPROP_SRC_INHERITED) smb_inherited = B_TRUE; else smb = B_TRUE; } /* * If the mountpoint is already shared, it must be a * non-ZFS share. We want to remove the share from its * parent group and reshare it under ZFS. */ share = sa_find_share(handle, mountpoint); if (share != NULL && (nfs || smb || nfs_inherited || smb_inherited)) { err = sa_remove_share(share); share = NULL; } /* * At this point, we have the information needed to * determine what to do with the share. * * If smb or nfs is set, we have a new sub-group. * If smb_inherit and/or nfs_inherit is set, then * place on an existing sub-group. If both are set, * the existing sub-group is the closest up the tree. */ if (nfs || smb) { /* * Non-inherited is the straightforward * case. sa_zfs_process_share handles it * directly. Make sure that if the "other" * protocol is inherited, that we treat it as * non-inherited as well. */ if (nfs || nfs_inherited) { err = sa_zfs_process_share(handle, zfsgroup, share, mountpoint, "nfs", 0, nfsshareopts, nfssourcestr, dataset); share = sa_find_share(handle, mountpoint); } if (smb || smb_inherited) { err = sa_zfs_process_share(handle, zfsgroup, share, mountpoint, "smb", 0, smbshareopts, smbsourcestr, dataset); } } else if (nfs_inherited || smb_inherited) { char *grpdataset; /* * If we only have inherited groups, it is * important to find the closer of the two if * the protocols are set at different * levels. The closest sub-group is the one we * want to work with. */ if (nfs_inherited && smb_inherited) { if (strcmp(nfssourcestr, smbsourcestr) <= 0) grpdataset = nfssourcestr; else grpdataset = smbsourcestr; } else if (nfs_inherited) { grpdataset = nfssourcestr; } else if (smb_inherited) { grpdataset = smbsourcestr; } if (nfs_inherited) { err = sa_zfs_process_share(handle, zfsgroup, share, mountpoint, "nfs", ZPROP_SRC_INHERITED, nfsshareopts, grpdataset, dataset); share = sa_find_share(handle, mountpoint); } if (smb_inherited) { err = sa_zfs_process_share(handle, zfsgroup, share, mountpoint, "smb", ZPROP_SRC_INHERITED, smbshareopts, grpdataset, dataset); } } } /* * Don't need to free the "zlist" variable since it is only a * pointer to a cached value that will be freed when * sa_fini() is called. */ return (err); }
/* * Function: be_add_children_callback * Description: Callback function used by zfs_iter to look through all * the datasets and snapshots for each BE and add them to * the lists of information to be passed back. * Parameters: * zhp - handle to the first zfs dataset. (provided by the * zfs_iter_* call) * data - pointer to the callback data and where we'll pass * the BE information back. * Returns: * 0 - Success * be_errno_t - Failure * Scope: * Private */ static int be_add_children_callback(zfs_handle_t *zhp, void *data) { list_callback_data_t *cb = (list_callback_data_t *)data; char *str = NULL, *ds_path = NULL; int ret = 0; struct be_defaults be_defaults; be_get_defaults(&be_defaults); ds_path = str = strdup(zfs_get_name(zhp)); /* * get past the end of the container dataset plus the trailing "/" */ str = str + (strlen(be_container_ds) + 1); if (be_defaults.be_deflt_rpool_container) { /* just skip if invalid */ if (!be_valid_be_name(str)) return (BE_SUCCESS); } if (cb->be_nodes_head == NULL) { if ((cb->be_nodes_head = be_list_alloc(&ret, sizeof (be_node_list_t))) == NULL) { ZFS_CLOSE(zhp); return (ret); } cb->be_nodes = cb->be_nodes_head; } if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && !zone_be) { be_snapshot_list_t *snapshots = NULL; if (cb->be_nodes->be_node_snapshots == NULL) { if ((cb->be_nodes->be_node_snapshots = be_list_alloc(&ret, sizeof (be_snapshot_list_t))) == NULL || ret != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } cb->be_nodes->be_node_snapshots->be_next_snapshot = NULL; snapshots = cb->be_nodes->be_node_snapshots; } else { for (snapshots = cb->be_nodes->be_node_snapshots; snapshots != NULL; snapshots = snapshots->be_next_snapshot) { if (snapshots->be_next_snapshot != NULL) continue; /* * We're at the end of the list add the * new snapshot. */ if ((snapshots->be_next_snapshot = be_list_alloc(&ret, sizeof (be_snapshot_list_t))) == NULL || ret != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } snapshots = snapshots->be_next_snapshot; snapshots->be_next_snapshot = NULL; break; } } if ((ret = be_get_ss_data(zhp, str, snapshots, cb->be_nodes)) != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } } else if (strchr(str, '/') == NULL) { if (cb->be_nodes->be_node_name != NULL) { if ((cb->be_nodes->be_next_node = be_list_alloc(&ret, sizeof (be_node_list_t))) == NULL || ret != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } cb->be_nodes = cb->be_nodes->be_next_node; cb->be_nodes->be_next_node = NULL; } /* * If this is a zone root dataset then we only need * the name of the zone BE at this point. We grab that * and return. */ if (zone_be) { ret = be_get_zone_node_data(cb->be_nodes, str); ZFS_CLOSE(zhp); return (ret); } if ((ret = be_get_node_data(zhp, cb->be_nodes, str, cb->zpool_name, cb->current_be, ds_path)) != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } } else if (strchr(str, '/') != NULL && !zone_be) { be_dataset_list_t *datasets = NULL; if (cb->be_nodes->be_node_datasets == NULL) { if ((cb->be_nodes->be_node_datasets = be_list_alloc(&ret, sizeof (be_dataset_list_t))) == NULL || ret != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } cb->be_nodes->be_node_datasets->be_next_dataset = NULL; datasets = cb->be_nodes->be_node_datasets; } else { for (datasets = cb->be_nodes->be_node_datasets; datasets != NULL; datasets = datasets->be_next_dataset) { if (datasets->be_next_dataset != NULL) continue; /* * We're at the end of the list add * the new dataset. */ if ((datasets->be_next_dataset = be_list_alloc(&ret, sizeof (be_dataset_list_t))) == NULL || ret != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } datasets = datasets->be_next_dataset; datasets->be_next_dataset = NULL; break; } } if ((ret = be_get_ds_data(zhp, str, datasets, cb->be_nodes)) != BE_SUCCESS) { ZFS_CLOSE(zhp); return (ret); } } ret = zfs_iter_children(zhp, be_add_children_callback, cb); if (ret != 0) { be_print_err(gettext("be_add_children_callback: " "encountered error: %s\n"), libzfs_error_description(g_zfs)); ret = zfs_err_to_be_err(g_zfs); } ZFS_CLOSE(zhp); return (ret); }
int zfs_crypto_load_key(zfs_handle_t *zhp, boolean_t noop, char *alt_keylocation) { int ret, attempts = 0; char errbuf[1024]; uint64_t keystatus, iters = 0, salt = 0; uint64_t keyformat = ZFS_KEYFORMAT_NONE; char prop_keylocation[MAXNAMELEN]; char prop_encroot[MAXNAMELEN]; char *keylocation = NULL; uint8_t *key_material = NULL, *key_data = NULL; size_t key_material_len; boolean_t is_encroot, can_retry = B_FALSE, correctible = B_FALSE; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Key load 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; } /* Fetch the keyformat. Check that the dataset is encrypted. */ keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); if (keyformat == ZFS_KEYFORMAT_NONE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "'%s' is not encrypted."), zfs_get_name(zhp)); ret = EINVAL; goto error; } /* * Fetch the key location. Check that we are working with an * encryption root. */ ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, prop_encroot); 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; } else if (!is_encroot) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Keys must be loaded for encryption root of '%s' (%s)."), zfs_get_name(zhp), prop_encroot); ret = EINVAL; goto error; } /* * if the caller has elected to override the keylocation property * use that instead */ if (alt_keylocation != NULL) { keylocation = alt_keylocation; } else { 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 keylocation for '%s'."), zfs_get_name(zhp)); goto error; } keylocation = prop_keylocation; } /* check that the key is unloaded unless this is a noop */ if (!noop) { keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); if (keystatus == ZFS_KEYSTATUS_AVAILABLE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Key already loaded for '%s'."), zfs_get_name(zhp)); ret = EEXIST; goto error; } } /* passphrase formats require a salt and pbkdf2_iters property */ if (keyformat == ZFS_KEYFORMAT_PASSPHRASE) { salt = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_SALT); iters = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_ITERS); } try_again: /* fetching and deriving the key are correctible errors. set the flag */ correctible = B_TRUE; /* get key material from key format and location */ ret = get_key_material(zhp->zfs_hdl, B_FALSE, B_FALSE, keyformat, keylocation, zfs_get_name(zhp), &key_material, &key_material_len, &can_retry); if (ret != 0) goto error; /* derive a key from the key material */ ret = derive_key(zhp->zfs_hdl, keyformat, iters, key_material, key_material_len, salt, &key_data); if (ret != 0) goto error; correctible = B_FALSE; /* pass the wrapping key and noop flag to the ioctl */ ret = lzc_load_key(zhp->zfs_name, noop, key_data, WRAPPING_KEY_LEN); 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 parameters provided for dataset %s."), zfs_get_name(zhp)); break; case EEXIST: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Key already loaded for '%s'."), zfs_get_name(zhp)); break; case EBUSY: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "'%s' is busy."), zfs_get_name(zhp)); break; case EACCES: correctible = B_TRUE; zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Incorrect key provided for '%s'."), zfs_get_name(zhp)); break; } goto error; } free(key_material); free(key_data); return (0); error: zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); if (key_material != NULL) free(key_material); if (key_data != NULL) free(key_data); /* * Here we decide if it is ok to allow the user to retry entering their * key. The can_retry flag will be set if the user is entering their * key from an interactive prompt. The correctible flag will only be * set if an error that occured could be corrected by retrying. Both * flags are needed to allow the user to attempt key entry again */ if (can_retry && correctible && attempts <= MAX_KEY_PROMPT_ATTEMPTS) { attempts++; goto try_again; } 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)); }
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)); }
/* * Share the given filesystem according to the options in the specified * protocol specific properties (sharenfs, sharesmb). We rely * on "libshare" to the dirty work for us. */ static int zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto) { char mountpoint[ZFS_MAXPROPLEN]; char shareopts[ZFS_MAXPROPLEN]; char sourcestr[ZFS_MAXPROPLEN]; #if defined(HAVE_ZPL) libzfs_handle_t *hdl = zhp->zfs_hdl; sa_share_t share; #endif zfs_share_proto_t *curr_proto; zprop_source_t sourcetype; #if defined(HAVE_ZPL) int ret; #endif if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); #if defined(HAVE_ZPL) if ((ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) { (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, dgettext(TEXT_DOMAIN, "cannot share '%s': %s"), zfs_get_name(zhp), _sa_errorstr != NULL ? _sa_errorstr(ret) : ""); return (-1); } #endif for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) { /* * Return success if there are no share options. */ if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop, shareopts, sizeof (shareopts), &sourcetype, sourcestr, ZFS_MAXPROPLEN, B_FALSE) != 0 || strcmp(shareopts, "off") == 0) continue; /* * If the 'zoned' property is set, then zfs_is_mountable() * will have already bailed out if we are in the global zone. * But local zones cannot be NFS servers, so we ignore it for * local zones as well. */ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) continue; #if !defined(HAVE_ZPL) if (*curr_proto == PROTO_NFS) { int pid; int rc; if ((pid = fork()) < 0) { fprintf(stderr, "cannot share '%s'", zfs_get_name(zhp)); return -1; } else if (pid == 0) { /* child process */ /* exec exportfs */ char export_string[PATH_MAX]; char options[100]; char *argv [] = { "exportfs", "-v", "-i", export_string, "-o", options, NULL }; struct statfs buf; int fsid_arr[2]; uint64_t fsid; if (statfs(mountpoint, &buf) < 0) return -1; memcpy((void *)fsid_arr, (void *) &buf.f_fsid, sizeof(int) * 2); fsid = fsid_arr[0]; fsid |= (((uint64_t)fsid_arr[1]) << 32); // fprintf(stderr, "using fsid=%lu\n", fsid); sprintf(export_string, "*:%s", mountpoint); sprintf(options, "rw,sync,fsid=%lu", fsid); execvp("exportfs", argv); return -1; } /* parent process */ if (waitpid(pid, &rc, WUNTRACED) != pid) { fprintf(stderr, "cannot share '%s'", zfs_get_name(zhp)); return -1; } if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0) { fprintf(stderr, "cannot share '%s'", zfs_get_name(zhp)); return -1; } } #else share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint); if (share == NULL) { /* * This may be a new file system that was just * created so isn't in the internal cache * (second time through). Rather than * reloading the entire configuration, we can * assume ZFS has done the checking and it is * safe to add this to the internal * configuration. */ if (_sa_zfs_process_share(hdl->libzfs_sharehdl, NULL, NULL, mountpoint, proto_table[*curr_proto].p_name, sourcetype, shareopts, sourcestr, zhp->zfs_name) != SA_OK) { (void) zfs_error_fmt(hdl, proto_table[*curr_proto].p_share_err, dgettext(TEXT_DOMAIN, "cannot share '%s'"), zfs_get_name(zhp)); return (-1); } hdl->libzfs_shareflags |= ZFSSHARE_MISS; share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint); } if (share != NULL) { int err; err = zfs_sa_enable_share(share, proto_table[*curr_proto].p_name); if (err != SA_OK) { (void) zfs_error_fmt(hdl, proto_table[*curr_proto].p_share_err, dgettext(TEXT_DOMAIN, "cannot share '%s'"), zfs_get_name(zhp)); return (-1); } } else { (void) zfs_error_fmt(hdl, proto_table[*curr_proto].p_share_err, dgettext(TEXT_DOMAIN, "cannot share '%s'"), zfs_get_name(zhp)); return (-1); } #endif } return (0); }
/* * Function: be_promote_ds_callback * Description: This function is used to promote the datasets for the BE * being activated as well as the datasets for the zones BE * being activated. * * Parameters: * zhp - the zfs handle for zone BE being activated. * data - not used. * Return: * 0 - Success * be_errno_t - Failure * * Scope: * Private */ static int /* LINTED */ be_promote_ds_callback(zfs_handle_t *zhp, void *data) { char origin[MAXPATHLEN]; char *sub_dataset = NULL; int ret = 0; if (zhp != NULL) { sub_dataset = strdup(zfs_get_name(zhp)); if (sub_dataset == NULL) { ret = BE_ERR_NOMEM; goto done; } } else { be_print_err(gettext("be_promote_ds_callback: " "Invalid zfs handle passed into function\n")); ret = BE_ERR_INVAL; goto done; } /* * This loop makes sure that we promote the dataset to the * top of the tree so that it is no longer a decendent of any * dataset. The ZFS close and then open is used to make sure that * the promotion is updated before we move on. */ while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) { if (zfs_promote(zhp) != 0) { if (libzfs_errno(g_zfs) != EZFS_EXISTS) { be_print_err(gettext("be_promote_ds_callback: " "promote of %s failed: %s\n"), zfs_get_name(zhp), libzfs_error_description(g_zfs)); ret = zfs_err_to_be_err(g_zfs); goto done; } else { /* * If the call to zfs_promote returns the * error EZFS_EXISTS we've hit a snapshot name * collision. This means we're probably * attemping to promote a zone dataset above a * parent dataset that belongs to another zone * which this zone was cloned from. * * TODO: If this is a zone dataset at some * point we should skip this if the zone * paths for the dataset and the snapshot * don't match. */ be_print_err(gettext("be_promote_ds_callback: " "promote of %s failed due to snapshot " "name collision: %s\n"), zfs_get_name(zhp), libzfs_error_description(g_zfs)); ret = zfs_err_to_be_err(g_zfs); goto done; } } ZFS_CLOSE(zhp); if ((zhp = zfs_open(g_zfs, sub_dataset, ZFS_TYPE_FILESYSTEM)) == NULL) { be_print_err(gettext("be_promote_ds_callback: " "Failed to open dataset (%s): %s\n"), sub_dataset, libzfs_error_description(g_zfs)); ret = zfs_err_to_be_err(g_zfs); goto done; } } /* Iterate down this dataset's children and promote them */ ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL); done: free(sub_dataset); ZFS_CLOSE(zhp); return (ret); }
/* * Given a ZFS handle and a property, construct a complete list of datasets * that need to be modified as part of this process. For anything but the * 'mountpoint' and 'sharenfs' properties, this just returns an empty list. * Otherwise, we iterate over all children and look for any datasets that * inherit the property. For each such dataset, we add it to the list and * mark whether it was shared beforehand. */ prop_changelist_t * changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) { prop_changelist_t *clp; prop_changenode_t *cn; zfs_handle_t *temp; char property[ZFS_MAXPROPLEN]; uu_compare_fn_t *compare = NULL; if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) return (NULL); /* * For mountpoint-related tasks, we want to sort everything by * mountpoint, so that we mount and unmount them in the appropriate * order, regardless of their position in the hierarchy. */ if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) { compare = compare_mountpoints; clp->cl_sorted = B_TRUE; } clp->cl_pool = uu_list_pool_create("changelist_pool", sizeof (prop_changenode_t), offsetof(prop_changenode_t, cn_listnode), compare, 0); if (clp->cl_pool == NULL) { assert(uu_error() == UU_ERROR_NO_MEMORY); (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); changelist_free(clp); return (NULL); } clp->cl_list = uu_list_create(clp->cl_pool, NULL, clp->cl_sorted ? UU_LIST_SORTED : 0); clp->cl_flags = flags; if (clp->cl_list == NULL) { assert(uu_error() == UU_ERROR_NO_MEMORY); (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); changelist_free(clp); return (NULL); } /* * If this is a rename or the 'zoned' property, we pretend we're * changing the mountpoint and flag it so we can catch all children in * change_one(). * * Flag cl_alldependents to catch all children plus the dependents * (clones) that are not in the hierarchy. */ if (prop == ZFS_PROP_NAME) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; clp->cl_alldependents = B_TRUE; } else if (prop == ZFS_PROP_ZONED) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; clp->cl_allchildren = B_TRUE; } else if (prop == ZFS_PROP_CANMOUNT) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else if (prop == ZFS_PROP_VOLSIZE) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else if (prop == ZFS_PROP_VERSION) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else { clp->cl_prop = prop; } clp->cl_realprop = prop; if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && clp->cl_prop != ZFS_PROP_SHARENFS && clp->cl_prop != ZFS_PROP_SHAREISCSI) return (clp); if (clp->cl_alldependents) { if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { changelist_free(clp); return (NULL); } } else if (zfs_iter_children(zhp, change_one, clp) != 0) { changelist_free(clp); return (NULL); } /* * We have to re-open ourselves because we auto-close all the handles * and can't tell the difference. */ if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), ZFS_TYPE_ANY)) == NULL) { changelist_free(clp); return (NULL); } /* * Always add ourself to the list. We add ourselves to the end so that * we're the last to be unmounted. */ if ((cn = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changenode_t))) == NULL) { zfs_close(temp); changelist_free(clp); return (NULL); } cn->cn_handle = temp; cn->cn_mounted = zfs_is_mounted(temp, NULL); cn->cn_shared = zfs_is_shared(temp); #ifndef __APPLE__ cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); #endif /*!__APPLE__*/ uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); if (clp->cl_sorted) { uu_list_index_t idx; (void) uu_list_find(clp->cl_list, cn, NULL, &idx); uu_list_insert(clp->cl_list, cn, idx); } else { verify(uu_list_insert_after(clp->cl_list, uu_list_last(clp->cl_list), cn) == 0); } /* * If the mountpoint property was previously 'legacy', or 'none', * record it as the behavior of changelist_postfix() will be different. */ if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && (zfs_prop_get(zhp, prop, property, sizeof (property), NULL, NULL, 0, B_FALSE) == 0 && (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0))) clp->cl_waslegacy = B_TRUE; return (clp); }
int zfs_crypto_unload_key(zfs_handle_t *zhp) { int ret; char errbuf[1024]; char prop_encroot[MAXNAMELEN]; uint64_t keystatus, keyformat; boolean_t is_encroot; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Key unload 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; } /* Fetch the keyformat. Check that the dataset is encrypted. */ keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); if (keyformat == ZFS_KEYFORMAT_NONE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "'%s' is not encrypted."), zfs_get_name(zhp)); ret = EINVAL; goto error; } /* * Fetch the key location. Check that we are working with an * encryption root. */ ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, prop_encroot); 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; } else if (!is_encroot) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Keys must be unloaded for encryption root of '%s' (%s)."), zfs_get_name(zhp), prop_encroot); ret = EINVAL; 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 already unloaded for '%s'."), zfs_get_name(zhp)); ret = ENOENT; goto error; } /* call the ioctl */ ret = lzc_unload_key(zhp->zfs_name); if (ret != 0) { switch (ret) { case EPERM: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Permission denied.")); break; case ENOENT: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Key already unloaded for '%s'."), zfs_get_name(zhp)); break; case EBUSY: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "'%s' is busy."), zfs_get_name(zhp)); break; } zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); } return (ret); error: zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); return (ret); }
/* * Given a ZFS handle and a property, construct a complete list of datasets * that need to be modified as part of this process. For anything but the * 'mountpoint' and 'sharenfs' properties, this just returns an empty list. * Otherwise, we iterate over all children and look for any datasets that * inherit the property. For each such dataset, we add it to the list and * mark whether it was shared beforehand. */ prop_changelist_t * changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags, int mnt_flags) { prop_changelist_t *clp; prop_changenode_t *cn; zfs_handle_t *temp; char property[ZFS_MAXPROPLEN]; uu_compare_fn_t *compare = NULL; boolean_t legacy = B_FALSE; if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) return (NULL); /* * For mountpoint-related tasks, we want to sort everything by * mountpoint, so that we mount and unmount them in the appropriate * order, regardless of their position in the hierarchy. */ if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS || prop == ZFS_PROP_SHARESMB) { if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property, sizeof (property), NULL, NULL, 0, B_FALSE) == 0 && (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0)) { legacy = B_TRUE; } if (!legacy) { compare = compare_mountpoints; clp->cl_sorted = B_TRUE; } } clp->cl_pool = uu_list_pool_create("changelist_pool", sizeof (prop_changenode_t), offsetof(prop_changenode_t, cn_listnode), compare, 0); if (clp->cl_pool == NULL) { assert(uu_error() == UU_ERROR_NO_MEMORY); (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); changelist_free(clp); return (NULL); } clp->cl_list = uu_list_create(clp->cl_pool, NULL, clp->cl_sorted ? UU_LIST_SORTED : 0); clp->cl_gflags = gather_flags; clp->cl_mflags = mnt_flags; if (clp->cl_list == NULL) { assert(uu_error() == UU_ERROR_NO_MEMORY); (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); changelist_free(clp); return (NULL); } /* * If this is a rename or the 'zoned' property, we pretend we're * changing the mountpoint and flag it so we can catch all children in * change_one(). * * Flag cl_alldependents to catch all children plus the dependents * (clones) that are not in the hierarchy. */ if (prop == ZFS_PROP_NAME) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; clp->cl_alldependents = B_TRUE; } else if (prop == ZFS_PROP_ZONED) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; clp->cl_allchildren = B_TRUE; } else if (prop == ZFS_PROP_CANMOUNT) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else if (prop == ZFS_PROP_VOLSIZE) { clp->cl_prop = ZFS_PROP_MOUNTPOINT; } else { clp->cl_prop = prop; } clp->cl_realprop = prop; if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && clp->cl_prop != ZFS_PROP_SHARENFS && clp->cl_prop != ZFS_PROP_SHARESMB) return (clp); /* * If watching SHARENFS or SHARESMB then * also watch its companion property. */ if (clp->cl_prop == ZFS_PROP_SHARENFS) clp->cl_shareprop = ZFS_PROP_SHARESMB; else if (clp->cl_prop == ZFS_PROP_SHARESMB) clp->cl_shareprop = ZFS_PROP_SHARENFS; if (clp->cl_alldependents) { if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { changelist_free(clp); return (NULL); } } else if (zfs_iter_children(zhp, change_one, clp) != 0) { changelist_free(clp); return (NULL); } /* * We have to re-open ourselves because we auto-close all the handles * and can't tell the difference. */ if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), ZFS_TYPE_DATASET)) == NULL) { changelist_free(clp); return (NULL); } /* * Always add ourself to the list. We add ourselves to the end so that * we're the last to be unmounted. */ if ((cn = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changenode_t))) == NULL) { zfs_close(temp); changelist_free(clp); return (NULL); } cn->cn_handle = temp; cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || zfs_is_mounted(temp, NULL); cn->cn_shared = zfs_is_shared(temp); cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); cn->cn_needpost = B_TRUE; uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); if (clp->cl_sorted) { uu_list_index_t idx; (void) uu_list_find(clp->cl_list, cn, NULL, &idx); uu_list_insert(clp->cl_list, cn, idx); } else { /* * Add the target dataset to the end of the list. * The list is not really unsorted. The list will be * in reverse dataset name order. This is necessary * when the original mountpoint is legacy or none. */ verify(uu_list_insert_after(clp->cl_list, uu_list_last(clp->cl_list), cn) == 0); } /* * If the mountpoint property was previously 'legacy', or 'none', * record it as the behavior of changelist_postfix() will be different. */ if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) { /* * do not automatically mount ex-legacy datasets if * we specifically set canmount to noauto */ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) != ZFS_CANMOUNT_NOAUTO) clp->cl_waslegacy = B_TRUE; } return (clp); }
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); }
/* * sa_get_zfs_info(libzfs, path, mountpoint, dataset) * * Find the ZFS dataset and mountpoint for a given path */ int sa_zfs_get_info(libzfs_handle_t *libzfs, char *path, char *mountpointp, char *datasetp) { get_all_cbdata_t cb = { 0 }; int i; char mountpoint[ZFS_MAXPROPLEN]; char dataset[ZFS_MAXPROPLEN]; char canmount[ZFS_MAXPROPLEN]; char *dp; int count; int ret = 0; cb.cb_types = ZFS_TYPE_FILESYSTEM; if (libzfs == NULL) return (0); (void) zfs_iter_root(libzfs, get_one_filesystem, &cb); count = cb.cb_used; qsort(cb.cb_handles, count, sizeof (void *), mountpoint_compare); for (i = 0; i < count; i++) { /* must have a mountpoint */ if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_MOUNTPOINT, mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { /* no mountpoint */ continue; } /* mountpoint must be a path */ if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { /* * Search mmttab for mountpoint */ if (get_legacy_mountpoint(path, dataset, ZFS_MAXPROPLEN, mountpoint, ZFS_MAXPROPLEN) == 0) { ret = 1; break; } continue; } /* canmount must be set */ canmount[0] = '\0'; if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_CANMOUNT, canmount, sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 || strcmp(canmount, "off") == 0) continue; /* * have a mountable handle but want to skip those marked none * and legacy */ if (strcmp(mountpoint, path) == 0) { dp = (char *)zfs_get_name(cb.cb_handles[i]); if (dp != NULL) { if (datasetp != NULL) (void) strcpy(datasetp, dp); if (mountpointp != NULL) (void) strcpy(mountpointp, mountpoint); ret = 1; } break; } } return (ret); }
static int populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, zfs_handle_t *zhp, boolean_t newkey, zfs_keyformat_t keyformat, char *keylocation, nvlist_t *props, uint8_t **wkeydata, uint_t *wkeylen) { int ret; uint64_t iters = 0, salt = 0; uint8_t *key_material = NULL; size_t key_material_len = 0; uint8_t *key_data = NULL; const char *fsname = (zhp) ? zfs_get_name(zhp) : NULL; /* get key material from keyformat and keylocation */ ret = get_key_material(hdl, B_TRUE, newkey, keyformat, keylocation, fsname, &key_material, &key_material_len, NULL); if (ret != 0) goto error; /* passphrase formats require a salt and pbkdf2 iters property */ if (keyformat == ZFS_KEYFORMAT_PASSPHRASE) { /* always generate a new salt */ random_init(); ret = random_get_bytes((uint8_t *)&salt, sizeof (uint64_t)); if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to generate salt.")); goto error; } random_fini(); ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), salt); if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to add salt to properties.")); goto error; } /* * If not otherwise specified, use the default number of * pbkdf2 iterations. If specified, we have already checked * that the given value is greater than MIN_PBKDF2_ITERATIONS * during zfs_valid_proplist(). */ ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters); if (ret == ENOENT) { iters = DEFAULT_PBKDF2_ITERATIONS; ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), iters); if (ret != 0) goto error; } else if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to get pbkdf2 iterations.")); goto error; } } else { /* check that pbkdf2iters was not specified by the user */ ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters); if (ret == 0) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Cannot specify pbkdf2iters with a non-passphrase " "keyformat.")); goto error; } } /* derive a key from the key material */ ret = derive_key(hdl, keyformat, iters, key_material, key_material_len, salt, &key_data); if (ret != 0) goto error; free(key_material); *wkeydata = key_data; *wkeylen = WRAPPING_KEY_LEN; return (0); error: if (key_material != NULL) free(key_material); if (key_data != NULL) free(key_data); *wkeydata = NULL; *wkeylen = 0; return (ret); }
boolean_t zfs_is_mounted(zfs_handle_t *zhp, char **where) { return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where)); }
/* * Mount the given filesystem. */ int zfs_mount(zfs_handle_t *zhp, const char *options, int flags) { struct stat buf; char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; libzfs_handle_t *hdl = zhp->zfs_hdl; if (options == NULL) mntopts[0] = '\0'; else (void) strlcpy(mntopts, options, sizeof (mntopts)); if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); /* Create the directory if it doesn't already exist */ if (lstat(mountpoint, &buf) != 0) { if (mkdirp(mountpoint, 0755) != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to create mountpoint")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } } /* * Determine if the mountpoint is empty. If so, refuse to perform the * mount. We don't perform this check if MS_OVERLAY is specified, which * would defeat the point. We also avoid this check if 'remount' is * specified. */ if ((flags & MS_OVERLAY) == 0 && strstr(mntopts, MNTOPT_REMOUNT) == NULL && !dir_is_empty(mountpoint)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "directory is not empty")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } /* perform the mount */ /* ZFSFUSE */ if (zfsfuse_mount(hdl, zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, MNTTYPE_ZFS, NULL, 0, mntopts, strlen (mntopts)) != 0) { /* * Generic errors are nasty, but there are just way too many * from mount(), and they're well-understood. We pick a few * common ones to improve upon. */ if (errno == EBUSY) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "mountpoint or dataset is busy")); } else if (errno == EPERM) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Insufficient privileges")); } else { zfs_error_aux(hdl, strerror(errno)); } return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), zhp->zfs_name)); } return (0); }
/* * Share the given filesystem according to the options in the specified * protocol specific properties (sharenfs, sharesmb). We rely * on "libshare" to the dirty work for us. */ static int zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto) { char mountpoint[ZFS_MAXPROPLEN]; char shareopts[ZFS_MAXPROPLEN]; char sourcestr[ZFS_MAXPROPLEN]; libzfs_handle_t *hdl = zhp->zfs_hdl; sa_share_t share; zfs_share_proto_t *curr_proto; zprop_source_t sourcetype; int ret; if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); if ((ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) { (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, dgettext(TEXT_DOMAIN, "cannot share '%s': %s"), zfs_get_name(zhp), _sa_errorstr != NULL ? _sa_errorstr(ret) : ""); return (-1); } for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) { /* * Return success if there are no share options. */ if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop, shareopts, sizeof (shareopts), &sourcetype, sourcestr, ZFS_MAXPROPLEN, B_FALSE) != 0 || strcmp(shareopts, "off") == 0) continue; /* * If the 'zoned' property is set, then zfs_is_mountable() * will have already bailed out if we are in the global zone. * But local zones cannot be NFS servers, so we ignore it for * local zones as well. */ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) continue; share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint); if (share == NULL) { /* * This may be a new file system that was just * created so isn't in the internal cache * (second time through). Rather than * reloading the entire configuration, we can * assume ZFS has done the checking and it is * safe to add this to the internal * configuration. */ if (_sa_zfs_process_share(hdl->libzfs_sharehdl, NULL, NULL, mountpoint, proto_table[*curr_proto].p_name, sourcetype, shareopts, sourcestr, zhp->zfs_name) != SA_OK) { (void) zfs_error_fmt(hdl, proto_table[*curr_proto].p_share_err, dgettext(TEXT_DOMAIN, "cannot share '%s'"), zfs_get_name(zhp)); return (-1); } hdl->libzfs_shareflags |= ZFSSHARE_MISS; share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint); } if (share != NULL) { int err; err = zfs_sa_enable_share(share, proto_table[*curr_proto].p_name); if (err != SA_OK) { (void) zfs_error_fmt(hdl, proto_table[*curr_proto].p_share_err, dgettext(TEXT_DOMAIN, "cannot share '%s'"), zfs_get_name(zhp)); return (-1); } } else { (void) zfs_error_fmt(hdl, proto_table[*curr_proto].p_share_err, dgettext(TEXT_DOMAIN, "cannot share '%s'"), zfs_get_name(zhp)); return (-1); } } return (0); }
/* * Mount the given filesystem. */ int zfs_mount(zfs_handle_t *zhp, const char *options, int flags) { struct stat buf; char mountpoint[ZFS_MAXPROPLEN]; char mntopts[MNT_LINE_MAX]; libzfs_handle_t *hdl = zhp->zfs_hdl; #ifdef __APPLE__ struct zfs_mount_args mnt_args = {0}; char devpath[MAXPATHLEN]; #endif if (options == NULL) mntopts[0] = '\0'; else (void) strlcpy(mntopts, options, sizeof (mntopts)); if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); #ifdef __APPLE__ /* * Check for optional paths */ if (hasoptionalpath(mntopts, "mountdev=", devpath, sizeof (devpath))) mnt_args.mountdev = devpath; if (hasoptionalpath(mntopts, "mountpoint=", mountpoint, sizeof (mountpoint))) goto callmount; #endif /* Create the directory if it doesn't already exist */ if (lstat(mountpoint, &buf) != 0) { if (mkdirp(mountpoint, 0755) != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to create mountpoint")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } } #ifndef __APPLE__ /* * Determine if the mountpoint is empty. If so, refuse to perform the * mount. We don't perform this check if MS_OVERLAY is specified, which * would defeat the point. We also avoid this check if 'remount' is * specified. */ if ((flags & MS_OVERLAY) == 0 && strstr(mntopts, MNTOPT_REMOUNT) == NULL && !dir_is_empty(mountpoint)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "directory is not empty")); return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); } #endif /*!__APPLE__*/ /* perform the mount */ #ifdef __APPLE__ callmount: mnt_args.dataset = zfs_get_name(zhp); mnt_args.flags = 0; if (mount(MNTTYPE_ZFS, mountpoint, flags, &mnt_args) != 0) { #else if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { #endif #ifdef __APPLE__ /* * If this was a top-level filesystem, then IOKit * probing may have already mounted it, causing * our call to mount() to fail. */ if (zfs_is_mounted(zhp, NULL)) { goto success; } #endif /* * Generic errors are nasty, but there are just way too many * from mount(), and they're well-understood. We pick a few * common ones to improve upon. */ if (errno == EBUSY) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "mountpoint or dataset is busy")); } else if (errno == EPERM) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Insufficient privileges")); } else { zfs_error_aux(hdl, strerror(errno)); } return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, dgettext(TEXT_DOMAIN, "cannot mount '%s'"), zhp->zfs_name)); } #ifdef __APPLE__ success: /* * Remove any legacy custom volume icons. */ { char path[MAXPATHLEN]; struct stat sbuf; snprintf(path, MAXPATHLEN, "%s/%s", mountpoint, MOUNT_POINT_CUSTOM_ICON); if (stat(path, &sbuf) == 0 && sbuf.st_size == 35014 && sbuf.st_uid == 0) { /* Clear "has custom icon" flag */ (void) removexattr(mountpoint, XATTR_FINDERINFO_NAME, 0); /* Clear custom icon file */ (void) unlink(path); } } #endif return (0); } #ifdef __APPLE__ /* * When unmounting, we first talk to diskarb and attempt to unmount via that * route. This allows diskarb to tell fsevents, mds, etc. to stop using the * filesystem. * * This code was borrowed from umount(8). */ #include <CoreFoundation/CoreFoundation.h> #include <TargetConditionals.h> #if !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE #include <DiskArbitration/DiskArbitrationPrivate.h> static void __diskarb_unmount( DADiskRef disk, DADissenterRef dissenter, void * context ) { *( ( int * ) context ) = dissenter ? DADissenterGetStatus( dissenter ) : 0; CFRunLoopStop( CFRunLoopGetCurrent( ) ); }