2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Handle initialization of the suspend subsystem. This includes setting
2N/A * platform default values for the suspend-enable property and creating
2N/A * the cpr file.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <libgen.h>
2N/A#include <sys/cpr.h>
2N/A#include <errno.h>
2N/A#include <sys/mkdev.h>
2N/A#include <sys/vtoc.h>
2N/A#include <sys/efi_partition.h>
2N/A#include <libnvpair.h>
2N/A#include <libuutil.h>
2N/A#include <libzfs.h>
2N/A#include <instzones_api.h>
2N/A#include <libpower.h>
2N/A#include <libpower_impl.h>
2N/A#ifdef sparc
2N/A#include <sys/openpromio.h>
2N/A#endif
2N/A
2N/Aint get_cpr_config_path(char **cpr_conf);
2N/A
2N/A#ifdef sparc
2N/Astatic int utop(char *fs_name, char *prom_name);
2N/A#endif
2N/A
2N/Astatic libzfs_handle_t *g_zfs = NULL;
2N/A
2N/Atypedef struct dir_data {
2N/A char *dir;
2N/A char *ds;
2N/A} dir_data_t;
2N/A
2N/A#define ZFS_CLOSE(_zhp) \
2N/A if (_zhp) { \
2N/A zfs_close(_zhp); \
2N/A _zhp = NULL; \
2N/A }
2N/A#define PM_POOL_SEP "/ROOT"
2N/A
2N/Astatic int be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data);
2N/Astatic char *be_get_ds_from_dir(char *dir);
2N/A
2N/A/*
2N/A * Initialize any suspend facilities for this machine if needed or configured.
2N/A */
2N/Apm_error_t
2N/Apm_init_suspend()
2N/A{
2N/A pm_error_t err;
2N/A boolean_t enabled = B_FALSE;
2N/A nvlist_t *proplist = NULL, *prop = NULL;
2N/A nvpair_t *nvp;
2N/A
2N/A /*
2N/A * Retrieve all of the SMF properties to see if suspend-enabled is
2N/A * already configured in SMF. If it is, then the service has been
2N/A * started before and no re-setting of the default is required.
2N/A */
2N/A errno = 0;
2N/A err = pm_smf_listprop(&proplist, PM_SVC_POWER);
2N/A if (err != PM_SUCCESS) {
2N/A /*
2N/A * An error occurred reading from SMF. Pass the error up
2N/A * to the caller to process.
2N/A */
2N/A return (err);
2N/A }
2N/A
2N/A if ((errno = nvlist_lookup_nvlist(proplist, PM_PROP_SUSPEND_ENABLE,
2N/A &prop)) != 0 || prop == NULL) {
2N/A /*
2N/A * The suspend-enable property is not yet initialized.
2N/A * Since this implies that suspend is not enabled,
2N/A * there is nothing left to do here, so return failure.
2N/A */
2N/A nvlist_free(proplist);
2N/A return (PM_ERROR_PROPERTY_NOT_FOUND);
2N/A }
2N/A
2N/A /* Recurse to find the value of the property */
2N/A if ((errno = nvlist_lookup_nvpair(prop, PM_AUTHORITY_SMF_STR,
2N/A &nvp)) == 0) {
2N/A errno = nvpair_value_boolean_value(nvp, &enabled);
2N/A }
2N/A
2N/A /* Clean up */
2N/A nvlist_free(prop);
2N/A nvlist_free(proplist);
2N/A
2N/A /* Things to run if suspend is enabled */
2N/A if (enabled && (pm_get_suspendenable() == B_TRUE)) {
2N/A /*
2N/A * Update the cprconfig file or create if it does not
2N/A * already exist.
2N/A */
2N/A if (update_cprconfig() != PM_SUCCESS) {
2N/A if (errno == ENOTSUP)
2N/A return (PM_ERROR_NOT_SUPPORTED);
2N/A else
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If we get here, always return success, as we have done
2N/A * what is needed to "initialize" suspend.
2N/A */
2N/A return (PM_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * returns B_TRUE if the slice is good (e.g. does not start at block
2N/A * zero, or a string describing the error if it doesn't
2N/A */
2N/Astatic boolean_t
2N/Ais_good_slice(char *sfile, char **err)
2N/A{
2N/A int fd, rc;
2N/A struct vtoc vtoc;
2N/A dk_gpt_t *gpt;
2N/A char rdskname[MAXPATHLEN];
2N/A char *x, *y;
2N/A
2N/A *err = NULL;
2N/A /* convert from dsk to rdsk */
2N/A (void) strlcpy(rdskname, sfile, sizeof (rdskname));
2N/A x = strstr(rdskname, "dsk/");
2N/A y = strstr(sfile, "dsk/");
2N/A if (x != NULL) {
2N/A *x++ = 'r';
2N/A (void) strcpy(x, y);
2N/A }
2N/A
2N/A if ((fd = open(rdskname, O_RDONLY)) == -1) {
2N/A *err = "could not open '%s'\n";
2N/A } else if ((rc = read_vtoc(fd, &vtoc)) >= 0) {
2N/A /*
2N/A * we got a slice number; now check the block
2N/A * number where the slice starts
2N/A */
2N/A if (vtoc.v_part[rc].p_start < 2)
2N/A *err = "using '%s' would clobber the disk label\n";
2N/A (void) close(fd);
2N/A return (*err ? B_FALSE : B_TRUE);
2N/A } else if ((rc == VT_ENOTSUP) &&
2N/A (efi_alloc_and_read(fd, &gpt)) >= 0) {
2N/A /* EFI slices don't clobber the disk label */
2N/A free(gpt);
2N/A (void) close(fd);
2N/A return (B_TRUE);
2N/A } else
2N/A *err = "could not read partition table from '%s'\n";
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * This should go in a header file, but for now it is private, so leave
2N/A * it here.
2N/A */
2N/A#define PM_PROP_STATEFILE "suspend-statefile"
2N/A
2N/A/*
2N/A * These describe the parent pathname to block disk devices used
2N/A * for the statefile. As there is no API for acquiring them, we
2N/A * define them here so that they are easily identified should they
2N/A * need to be updated.
2N/A */
2N/A#define PM_SPECFSPATH "/dev/dsk/"
2N/A#define PM_ZVOLPATH "/dev/zvol/dsk/"
2N/A#define PM_DUMPNAME "dump"
2N/A
2N/A/*
2N/A * Get and verify the device for saving a statefile.
2N/A * If there is not a defined property, choose the ZFS dump device.
2N/A */
2N/Astatic void
2N/Asfpath(struct cprconfig *cc) {
2N/A char sfile[MAXPATHLEN], diskname[MAXPATHLEN];
2N/A char pool_name[MAXPATHLEN];
2N/A char *p, *vname, *fp = NULL, *err_fmt = NULL;
2N/A size_t size;
2N/A struct stat stbuf;
2N/A nvlist_t *proplist = NULL, *prop = NULL;
2N/A nvpair_t *nvp;
2N/A zpool_handle_t *zpool_handle;
2N/A nvlist_t *config, *nvroot;
2N/A nvlist_t **child;
2N/A uint_t children;
2N/A libzfs_handle_t *lzfs;
2N/A
2N/A /*
2N/A * First see if we have a property that specifies the
2N/A * statefile path. If any attempt to find it via SMF
2N/A * fails, fallback to the "default" path of the ZFS dump
2N/A * device.
2N/A */
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s get statefile path\n", __FUNCTION__);
2N/A if (pm_smf_listprop(&proplist, PM_SVC_POWER) == PM_SUCCESS) {
2N/A /* Get the property defining the statefile path */
2N/A if (nvlist_lookup_nvlist(proplist, PM_PROP_STATEFILE,
2N/A &prop) == 0 && prop != NULL) {
2N/A /*
2N/A * We have a property, so now extract the value
2N/A * from the property
2N/A */
2N/A if (nvlist_lookup_nvpair(prop, PM_AUTHORITY_SMF_STR,
2N/A &nvp) == 0 && nvp != NULL) {
2N/A (void) nvpair_value_string(nvp, &fp);
2N/A (void) strlcpy(sfile, fp, MAXPATHLEN);
2N/A }
2N/A nvlist_free(prop);
2N/A }
2N/A nvlist_free(proplist);
2N/A }
2N/A
2N/A /*
2N/A * Define the "default" statefile.
2N/A * Note that this suffers from the same problem as
2N/A * get_cpr_config_path(), in that it uses a copy of
2N/A * a private interface to get the pool name.
2N/A * Technically, we have free'd the stuff at fp, but that
2N/A * is OK, as we don't expect to use the data, but only know
2N/A * that we found data.
2N/A */
2N/A if ((fp == NULL) && ((fp = be_get_ds_from_dir("/")) != NULL)) {
2N/A if ((p = strstr(fp, PM_POOL_SEP)) != NULL) {
2N/A (void) memset(pool_name, 0, MAXPATHLEN);
2N/A size = (size_t)(p - fp);
2N/A (void) strlcpy(pool_name, fp, size);
2N/A (void) snprintf(sfile, MAXPATHLEN,
2N/A "%s%s/%s", PM_ZVOLPATH, pool_name, PM_DUMPNAME);
2N/A } else {
2N/A /* Couldn't find pool, return. */
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s statefile requires zfs\n", __FUNCTION__);
2N/A return;
2N/A }
2N/A }
2N/A
2N/A /* If we still cannot find a path, return. */
2N/A if (fp == NULL) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s statefile not specified\n", __FUNCTION__);
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Statefile must exist before it can be used here.
2N/A */
2N/A if (stat(sfile, &stbuf) == -1) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s statefile path error on %s\n", __FUNCTION__, sfile);
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * If not a block device, return.
2N/A */
2N/A if (!S_ISBLK(stbuf.st_mode)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s statefile not block device\n", __FUNCTION__);
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * If not a good "slice", return.
2N/A */
2N/A if (!is_good_slice(sfile, &err_fmt)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s %s\n", __FUNCTION__, err_fmt);
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * OK, we have a block device, and we know it is valid,
2N/A * so identify the prom device
2N/A * If the path is *not* a zvol, it will be treated as a
2N/A * SPECFS device.
2N/A */
2N/A if (strncmp(sfile, PM_ZVOLPATH, sizeof (PM_ZVOLPATH) - 1) != 0) {
2N/A#ifdef sparc
2N/A if (utop(sfile, cc->cf_dev_prom)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s could not convert to prom name: %s\n",
2N/A __FUNCTION__, sfile);
2N/A return;
2N/A }
2N/A#else
2N/A (void) strlcpy(cc->cf_dev_prom, sfile,
2N/A sizeof (cc->cf_dev_prom));
2N/A#endif
2N/A cc->cf_type = CFT_SPEC;
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s statefile is specfs device %s\n", __FUNCTION__,
2N/A cc->cf_dev_prom);
2N/A } else {
2N/A /*
2N/A * Should be ZFS, get the underlying device.
2N/A * If anything fails, just return as we haven't yet
2N/A * filled in any config file entries.
2N/A * Note that we don't really care about the vdev on
2N/A * x86 platforms, but this code does give us the
2N/A * oppertunity to validate the zvol.
2N/A */
2N/A fp = sfile;
2N/A fp += sizeof (PM_ZVOLPATH) - 1;
2N/A (void) strncpy(pool_name, fp, MAXPATHLEN);
2N/A if (p = strchr(pool_name, '/'))
2N/A *p = '\0';
2N/A
2N/A if ((lzfs = libzfs_init()) == NULL) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s couldn't init zfs\n", __FUNCTION__);
2N/A return;
2N/A }
2N/A if ((zpool_handle = zpool_open(lzfs, pool_name)) == NULL) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s couldn't open zfs\n", __FUNCTION__);
2N/A libzfs_fini(lzfs);
2N/A return;
2N/A }
2N/A config = zpool_get_config(zpool_handle, NULL);
2N/A if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
2N/A &nvroot) != 0) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s couldn't identify pool %s\n", __FUNCTION__,
2N/A pool_name);
2N/A zpool_close(zpool_handle);
2N/A libzfs_fini(lzfs);
2N/A return;
2N/A }
2N/A verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
2N/A &child, &children) == 0);
2N/A if (children != 1) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s couldn't get pool children\n", __FUNCTION__);
2N/A zpool_close(zpool_handle);
2N/A libzfs_fini(lzfs);
2N/A return;
2N/A }
2N/A vname = zpool_vdev_name(lzfs, zpool_handle, child[0], B_FALSE,
2N/A B_FALSE);
2N/A if (vname == NULL) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s couldn't identify vdev\n", __FUNCTION__);
2N/A zpool_close(zpool_handle);
2N/A libzfs_fini(lzfs);
2N/A return;
2N/A }
2N/A (void) strcpy(diskname, PM_SPECFSPATH);
2N/A (void) strlcat(diskname, vname, sizeof (diskname));
2N/A#ifdef sparc
2N/A if (utop(diskname, cc->cf_dev_prom)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s could not convert to prom name: %s\n",
2N/A __FUNCTION__, sfile);
2N/A return;
2N/A }
2N/A#else
2N/A (void) strlcpy(cc->cf_dev_prom, diskname,
2N/A sizeof (cc->cf_dev_prom));
2N/A#endif
2N/A (void) strncpy(cc->cf_fs, p + 1, sizeof (p + 1));
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s statefile is zfs device: %s\n", __FUNCTION__,
2N/A cc->cf_dev_prom);
2N/A cc->cf_type = CFT_ZVOL;
2N/A free(vname);
2N/A zpool_close(zpool_handle);
2N/A libzfs_fini(lzfs);
2N/A }
2N/A
2N/A /*
2N/A * By virtue of arriving here, we have identified and validated
2N/A * the statefile path. All that remains is to stuff it in the
2N/A * last two config file entries.
2N/A */
2N/A (void) strlcpy(cc->cf_devfs, sfile, sizeof (cc->cf_devfs));
2N/A (void) strlcpy(cc->cf_path, sfile, sizeof (cc->cf_path));
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s statefile path: %s\n", __FUNCTION__, sfile);
2N/A}
2N/A
2N/A/*
2N/A * Update or create cpr_config file.
2N/A */
2N/Apm_error_t
2N/Aupdate_cprconfig()
2N/A{
2N/A struct cprconfig cc;
2N/A char cpr_conf_dir[MAXPATHLEN], cpr_conf_parent[MAXPATHLEN];
2N/A int fd, createdir = 0;
2N/A struct stat statbuf;
2N/A char *cpr_conf = NULL;
2N/A ssize_t nread;
2N/A
2N/A (void) memset(&cc, 0, sizeof (cc));
2N/A
2N/A /*
2N/A * A note on the use of dirname and the strings we use to get it.
2N/A * dirname(3c) usually manipulates the string that is passed in,
2N/A * such that the original string is not usable unless it was
2N/A * dup'd or copied before. The exception, is if a NULL is passed,
2N/A * and then we get get a new string that is ".". So we must
2N/A * always check the return value, and if we care about the original
2N/A * string, we must dup it before calling dirname.
2N/A */
2N/A
2N/A if (cpr_conf == NULL && get_cpr_config_path(&cpr_conf) != 0) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s cannot identify cpr_file\n", __FUNCTION__);
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A
2N/A if (strlcpy(cpr_conf_dir, cpr_conf, MAXPATHLEN) >= MAXPATHLEN) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s buffer overflow error\n", __FUNCTION__);
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A
2N/A if ((strcmp(dirname(cpr_conf_dir), ".") == 0) ||
2N/A (cpr_conf_dir[0] != '/')) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s cpr_config directory unknown or invalid\n",
2N/A __FUNCTION__);
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A
2N/A /*
2N/A * If the confdir doesn't exist, it needs to be created,
2N/A * unless the parent doesn't exist, and then we bail.
2N/A */
2N/A if (stat(cpr_conf_dir, &statbuf) == -1) {
2N/A /*
2N/A * cpr_config directory doesn't exist. If the parent does,
2N/A * set it up so that cpr_conf_dir can be created a little
2N/A * further down. Otherwise, return an error.
2N/A */
2N/A if (strlcpy(cpr_conf_parent, cpr_conf_dir, MAXPATHLEN) >=
2N/A MAXPATHLEN) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s buffer overflow error\n", __FUNCTION__);
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A /*
2N/A * We already know that cpr_conf_dir properly started
2N/A * with '/', so the copy should as well, and we don't
2N/A * need to test for it. However, if the parent is
2N/A * just '/', this is an error.
2N/A */
2N/A if ((strcmp(dirname(cpr_conf_parent), "/") == 0) ||
2N/A (stat(cpr_conf_parent, &statbuf) == -1)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s cpr_config parent directory missing\n",
2N/A __FUNCTION__);
2N/A return (PM_ERROR_SYSTEM);
2N/A } else {
2N/A createdir = 1;
2N/A }
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Create the cpr_config directory if we have determined it is missing
2N/A */
2N/A if (createdir) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG, "%s creating: %s\n",
2N/A __FUNCTION__, cpr_conf_dir);
2N/A
2N/A if (mkdir(cpr_conf_dir, 0755) == -1) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s conf directory %s missing\n",
2N/A __FUNCTION__, cpr_conf_dir);
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A }
2N/A
2N/A /* Is there an existing cpr_config */
2N/A if (stat(cpr_conf, &statbuf) != -1) {
2N/A /* Use it to initially populate cprconfig struct. */
2N/A if ((statbuf.st_size >= sizeof (cc)) &&
2N/A (fd = open(cpr_conf, O_RDONLY)) != -1) {
2N/A nread = read(fd, &cc, sizeof (cc));
2N/A if (nread != (ssize_t)sizeof (cc)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG,
2N/A "%s: %s is corrupted, regenerating.\n",
2N/A __FUNCTION__, cpr_conf);
2N/A (void) memset(&cc, 0, sizeof (cc));
2N/A }
2N/A }
2N/A (void) close(fd);
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG, "%s updating file: %s\n",
2N/A __FUNCTION__, cpr_conf);
2N/A } else {
2N/A uu_dprintf(pm_log, UU_DPRINTF_DEBUG, "%s creating file: %s\n",
2N/A __FUNCTION__, cpr_conf);
2N/A }
2N/A
2N/A /* Always make sure the magic number is correct. */
2N/A cc.cf_magic = CPR_CONFIG_MAGIC;
2N/A
2N/A /*
2N/A * Fetch the statefile path for this platform, and populate
2N/A * the cpr config struct.
2N/A */
2N/A sfpath(&cc);
2N/A
2N/A /*
2N/A * Effectively, we always want a fresh file, so create it if
2N/A * it doesn't exist, and truncate it if it does.
2N/A */
2N/A if ((fd = open(cpr_conf, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s cannot open/create \"%s\", %s\\n",
2N/A __FUNCTION__, cpr_conf, strerror(errno));
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A
2N/A if (write(fd, &cc, sizeof (cc)) != sizeof (cc)) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s error writing \"%s\", %s\\n",
2N/A __FUNCTION__, cpr_conf, strerror(errno));
2N/A (void) close(fd);
2N/A return (PM_ERROR_SYSTEM);
2N/A }
2N/A
2N/A (void) close(fd);
2N/A return (PM_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Find the path to the cpr_config file.
2N/A */
2N/Aint
2N/Aget_cpr_config_path(char **cpr_conf)
2N/A{
2N/A int cpsize;
2N/A#ifdef sparc
2N/A /* On Sparc, this is easy, as it (currently) must be CPR_CONFIG */
2N/A if (*cpr_conf == NULL) {
2N/A *cpr_conf = strdup(CPR_CONFIG);
2N/A cpsize = strlen(CPR_CONFIG);
2N/A } else {
2N/A cpsize = snprintf(*cpr_conf, MAXPATHLEN, "%s", CPR_CONFIG);
2N/A }
2N/A#else
2N/A char pool[MAXPATHLEN], *fs, *sep;
2N/A size_t size;
2N/A
2N/A /* On others, we need to find the pool the root is running. */
2N/A
2N/A /* Clear and add the leading '/' to the pool pathname. */
2N/A (void) memset(pool, 0, MAXPATHLEN);
2N/A if (*cpr_conf == NULL)
2N/A *cpr_conf = malloc(MAXPATHLEN);
2N/A pool[0] = '/';
2N/A
2N/A if ((fs = be_get_ds_from_dir("/")) == NULL)
2N/A return (-1);
2N/A
2N/A if ((sep = strstr(fs, PM_POOL_SEP)) != NULL) {
2N/A /*
2N/A * We have identified the mounted pool, extract everything
2N/A * up to "/ROOT" from it.
2N/A */
2N/A size = (size_t)(sep - fs);
2N/A (void) strncpy(pool + 1, fs, size);
2N/A }
2N/A
2N/A cpsize = snprintf(*cpr_conf, MAXPATHLEN, "%s%s", pool, CPR_CONFIG);
2N/A#endif
2N/A if ((cpsize <= 0) || (cpsize > MAXPATHLEN))
2N/A return (-1);
2N/A else
2N/A return (0);
2N/A}
2N/A
2N/A#ifdef sparc
2N/A/*
2N/A * Convert a Unix device to a prom device and save on success,
2N/A * log any ioctl/conversion error.
2N/A */
2N/Astatic int
2N/Autop(char *fs_name, char *prom_name)
2N/A{
2N/A union obpbuf {
2N/A char buf[OBP_MAXPATHLEN + sizeof (uint_t)];
2N/A struct openpromio oppio;
2N/A };
2N/A union obpbuf oppbuf;
2N/A struct openpromio *opp;
2N/A char *promdev = "/dev/openprom";
2N/A int fd, upval;
2N/A
2N/A if ((fd = open(promdev, O_RDONLY)) == -1) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s cannot open \"%s\", %s.\n",
2N/A __FUNCTION__, promdev, strerror(errno));
2N/A return (-1);
2N/A }
2N/A
2N/A opp = &oppbuf.oppio;
2N/A opp->oprom_size = OBP_MAXPATHLEN;
2N/A (void) strlcpy(opp->oprom_array, fs_name, OBP_MAXPATHLEN);
2N/A upval = ioctl(fd, OPROMDEV2PROMNAME, opp);
2N/A (void) close(fd);
2N/A if (upval == 0) {
2N/A (void) strlcpy(prom_name, opp->oprom_array, OBP_MAXPATHLEN);
2N/A } else {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s cannot convert \"%s\" to prom device\n",
2N/A __FUNCTION__, fs_name);
2N/A }
2N/A
2N/A return (upval);
2N/A}
2N/A
2N/A#endif
2N/A
2N/A/*
2N/A * Function: be_get_ds_from_dir_callback
2N/A * Description: This is a callback function used to iterate all datasets
2N/A * to find the one that is currently mounted at the directory
2N/A * being searched for. If matched, the name of the dataset is
2N/A * returned in heap storage, so the caller is responsible for
2N/A * freeing it.
2N/A * Parameters:
2N/A * zhp - zfs_handle_t pointer to current dataset being processed.
2N/A * data - dir_data_t pointer providing name of directory being
2N/A * searched for.
2N/A * Returns:
2N/A * 1 - This dataset is mounted at directory being searched for.
2N/A * 0 - This dataset is not mounted at directory being searched for.
2N/A * Scope:
2N/A * Private
2N/A */
2N/Astatic int
2N/Abe_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
2N/A{
2N/A dir_data_t *dd = data;
2N/A char *mp = NULL;
2N/A int zret = 0;
2N/A
2N/A if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2N/A ZFS_CLOSE(zhp);
2N/A return (0);
2N/A }
2N/A
2N/A if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
2N/A strcmp(mp, dd->dir) == 0) {
2N/A if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
2N/A uu_dprintf(pm_log, UU_DPRINTF_FATAL,
2N/A "%s memory allocation failed\n", __FUNCTION__);
2N/A ZFS_CLOSE(zhp);
2N/A return (0);
2N/A }
2N/A ZFS_CLOSE(zhp);
2N/A return (1);
2N/A }
2N/A
2N/A zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
2N/A
2N/A ZFS_CLOSE(zhp);
2N/A
2N/A return (zret);
2N/A}
2N/A
2N/A/*
2N/A * Function: be_get_ds_from_dir(char *dir)
2N/A * Description: Given a directory path, find the underlying dataset mounted
2N/A * at that directory path if there is one. The returned name
2N/A * is allocated in heap storage, so the caller is responsible
2N/A * for freeing it.
2N/A * Parameters:
2N/A * dir - char pointer of directory to find.
2N/A * Returns:
2N/A * NULL - if directory is not mounted from a dataset.
2N/A * name of dataset mounted at dir.
2N/A * Scope:
2N/A * Semi-private (library wide use only)
2N/A *
2N/A * This is what I need to find the root pool from a mounted root dir.
2N/A */
2N/Astatic char *
2N/Abe_get_ds_from_dir(char *dir)
2N/A{
2N/A dir_data_t dd = { 0 };
2N/A char resolved_dir[MAXPATHLEN];
2N/A
2N/A if (g_zfs == NULL && (g_zfs = libzfs_init()) == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Make sure length of dir is within the max length */
2N/A if (dir == NULL || strlen(dir) >= MAXPATHLEN)
2N/A return (NULL);
2N/A
2N/A /* Resolve dir in case its lofs mounted */
2N/A (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
2N/A z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
2N/A
2N/A dd.dir = resolved_dir;
2N/A
2N/A (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
2N/A
2N/A return (dd.ds);
2N/A}