Logo Search packages:      
Sourcecode: zfs-fuse version File versions  Download package

libzfs_util.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Internal utility routines for the ZFS library.
 */

#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/types.h>

#include <libzfs.h>
#include <zfsfuse.h>

#include "libzfs_impl.h"
#include "zfs_prop.h"

int
libzfs_errno(libzfs_handle_t *hdl)
{
      return (hdl->libzfs_error);
}

const char *
libzfs_error_action(libzfs_handle_t *hdl)
{
      return (hdl->libzfs_action);
}

const char *
libzfs_error_description(libzfs_handle_t *hdl)
{
      if (hdl->libzfs_desc[0] != '\0')
            return (hdl->libzfs_desc);

      switch (hdl->libzfs_error) {
      case EZFS_NOMEM:
            return (dgettext(TEXT_DOMAIN, "out of memory"));
      case EZFS_BADPROP:
            return (dgettext(TEXT_DOMAIN, "invalid property value"));
      case EZFS_PROPREADONLY:
            return (dgettext(TEXT_DOMAIN, "read only property"));
      case EZFS_PROPTYPE:
            return (dgettext(TEXT_DOMAIN, "property doesn't apply to "
                "datasets of this type"));
      case EZFS_PROPNONINHERIT:
            return (dgettext(TEXT_DOMAIN, "property cannot be inherited"));
      case EZFS_PROPSPACE:
            return (dgettext(TEXT_DOMAIN, "invalid quota or reservation"));
      case EZFS_BADTYPE:
            return (dgettext(TEXT_DOMAIN, "operation not applicable to "
                "datasets of this type"));
      case EZFS_BUSY:
            return (dgettext(TEXT_DOMAIN, "pool or dataset is busy"));
      case EZFS_EXISTS:
            return (dgettext(TEXT_DOMAIN, "pool or dataset exists"));
      case EZFS_NOENT:
            return (dgettext(TEXT_DOMAIN, "no such pool or dataset"));
      case EZFS_BADSTREAM:
            return (dgettext(TEXT_DOMAIN, "invalid backup stream"));
      case EZFS_DSREADONLY:
            return (dgettext(TEXT_DOMAIN, "dataset is read only"));
      case EZFS_VOLTOOBIG:
            return (dgettext(TEXT_DOMAIN, "volume size exceeds limit for "
                "this system"));
      case EZFS_VOLHASDATA:
            return (dgettext(TEXT_DOMAIN, "volume has data"));
      case EZFS_INVALIDNAME:
            return (dgettext(TEXT_DOMAIN, "invalid name"));
      case EZFS_BADRESTORE:
            return (dgettext(TEXT_DOMAIN, "unable to restore to "
                "destination"));
      case EZFS_BADBACKUP:
            return (dgettext(TEXT_DOMAIN, "backup failed"));
      case EZFS_BADTARGET:
            return (dgettext(TEXT_DOMAIN, "invalid target vdev"));
      case EZFS_NODEVICE:
            return (dgettext(TEXT_DOMAIN, "no such device in pool"));
      case EZFS_BADDEV:
            return (dgettext(TEXT_DOMAIN, "invalid device"));
      case EZFS_NOREPLICAS:
            return (dgettext(TEXT_DOMAIN, "no valid replicas"));
      case EZFS_RESILVERING:
            return (dgettext(TEXT_DOMAIN, "currently resilvering"));
      case EZFS_BADVERSION:
            return (dgettext(TEXT_DOMAIN, "unsupported version"));
      case EZFS_POOLUNAVAIL:
            return (dgettext(TEXT_DOMAIN, "pool is unavailable"));
      case EZFS_DEVOVERFLOW:
            return (dgettext(TEXT_DOMAIN, "too many devices in one vdev"));
      case EZFS_BADPATH:
            return (dgettext(TEXT_DOMAIN, "must be an absolute path"));
      case EZFS_CROSSTARGET:
            return (dgettext(TEXT_DOMAIN, "operation crosses datasets or "
                "pools"));
      case EZFS_ZONED:
            return (dgettext(TEXT_DOMAIN, "dataset in use by local zone"));
      case EZFS_MOUNTFAILED:
            return (dgettext(TEXT_DOMAIN, "mount failed"));
      case EZFS_UMOUNTFAILED:
            return (dgettext(TEXT_DOMAIN, "umount failed"));
      case EZFS_UNSHARENFSFAILED:
            return (dgettext(TEXT_DOMAIN, "unshare(1M) failed"));
      case EZFS_SHARENFSFAILED:
            return (dgettext(TEXT_DOMAIN, "share(1M) failed"));
      case EZFS_UNSHARESMBFAILED:
            return (dgettext(TEXT_DOMAIN, "smb remove share failed"));
      case EZFS_SHARESMBFAILED:
            return (dgettext(TEXT_DOMAIN, "smb add share failed"));
      case EZFS_ISCSISVCUNAVAIL:
            return (dgettext(TEXT_DOMAIN,
                "iscsitgt service need to be enabled by "
                "a privileged user"));
      case EZFS_DEVLINKS:
            return (dgettext(TEXT_DOMAIN, "failed to create /dev links"));
      case EZFS_PERM:
            return (dgettext(TEXT_DOMAIN, "permission denied"));
      case EZFS_NOSPC:
            return (dgettext(TEXT_DOMAIN, "out of space"));
      case EZFS_IO:
            return (dgettext(TEXT_DOMAIN, "I/O error"));
      case EZFS_INTR:
            return (dgettext(TEXT_DOMAIN, "signal received"));
      case EZFS_ISSPARE:
            return (dgettext(TEXT_DOMAIN, "device is reserved as a hot "
                "spare"));
      case EZFS_INVALCONFIG:
            return (dgettext(TEXT_DOMAIN, "invalid vdev configuration"));
      case EZFS_RECURSIVE:
            return (dgettext(TEXT_DOMAIN, "recursive dataset dependency"));
      case EZFS_NOHISTORY:
            return (dgettext(TEXT_DOMAIN, "no history available"));
      case EZFS_UNSHAREISCSIFAILED:
            return (dgettext(TEXT_DOMAIN,
                "iscsitgtd failed request to unshare"));
      case EZFS_SHAREISCSIFAILED:
            return (dgettext(TEXT_DOMAIN,
                "iscsitgtd failed request to share"));
      case EZFS_POOLPROPS:
            return (dgettext(TEXT_DOMAIN, "failed to retrieve "
                "pool properties"));
      case EZFS_POOL_NOTSUP:
            return (dgettext(TEXT_DOMAIN, "operation not supported "
                "on this type of pool"));
      case EZFS_POOL_INVALARG:
            return (dgettext(TEXT_DOMAIN, "invalid argument for "
                "this pool operation"));
      case EZFS_NAMETOOLONG:
            return (dgettext(TEXT_DOMAIN, "dataset name is too long"));
      case EZFS_OPENFAILED:
            return (dgettext(TEXT_DOMAIN, "open failed"));
      case EZFS_NOCAP:
            return (dgettext(TEXT_DOMAIN,
                "disk capacity information could not be retrieved"));
      case EZFS_LABELFAILED:
            return (dgettext(TEXT_DOMAIN, "write of label failed"));
      case EZFS_BADWHO:
            return (dgettext(TEXT_DOMAIN, "invalid user/group"));
      case EZFS_BADPERM:
            return (dgettext(TEXT_DOMAIN, "invalid permission"));
      case EZFS_BADPERMSET:
            return (dgettext(TEXT_DOMAIN, "invalid permission set name"));
      case EZFS_NODELEGATION:
            return (dgettext(TEXT_DOMAIN, "delegated administration is "
                "disabled on pool"));
      case EZFS_PERMRDONLY:
            return (dgettext(TEXT_DOMAIN, "snapshot permissions cannot be"
                " modified"));
      case EZFS_BADCACHE:
            return (dgettext(TEXT_DOMAIN, "invalid or missing cache file"));
      case EZFS_ISL2CACHE:
            return (dgettext(TEXT_DOMAIN, "device is in use as a cache"));
      case EZFS_VDEVNOTSUP:
            return (dgettext(TEXT_DOMAIN, "vdev specification is not "
                "supported"));
      case EZFS_NOTSUP:
            return (dgettext(TEXT_DOMAIN, "operation not supported "
                "on this dataset"));
      case EZFS_ACTIVE_SPARE:
            return (dgettext(TEXT_DOMAIN, "pool has active shared spare "
                "device"));
      case EZFS_UNPLAYED_LOGS:
            return (dgettext(TEXT_DOMAIN, "log device has unplayed intent "
                "logs"));
      case EZFS_UNKNOWN:
            return (dgettext(TEXT_DOMAIN, "unknown error"));
      default:
            assert(hdl->libzfs_error == 0);
            return (dgettext(TEXT_DOMAIN, "no error"));
      }
}

/*PRINTFLIKE2*/
void
zfs_error_aux(libzfs_handle_t *hdl, const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);

      (void) vsnprintf(hdl->libzfs_desc, sizeof (hdl->libzfs_desc),
          fmt, ap);
      hdl->libzfs_desc_active = 1;

      va_end(ap);
}

static void
zfs_verror(libzfs_handle_t *hdl, int error, const char *fmt, va_list ap)
{
      (void) vsnprintf(hdl->libzfs_action, sizeof (hdl->libzfs_action),
          fmt, ap);
      hdl->libzfs_error = error;

      if (hdl->libzfs_desc_active)
            hdl->libzfs_desc_active = 0;
      else
            hdl->libzfs_desc[0] = '\0';

      if (hdl->libzfs_printerr) {
            if (error == EZFS_UNKNOWN) {
                  (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "internal "
                      "error: %s\n"), libzfs_error_description(hdl));
                  abort();
            }

            (void) fprintf(stderr, "%s: %s\n", hdl->libzfs_action,
                libzfs_error_description(hdl));
            if (error == EZFS_NOMEM)
                  exit(1);
      }
}

int
zfs_error(libzfs_handle_t *hdl, int error, const char *msg)
{
      return (zfs_error_fmt(hdl, error, "%s", msg));
}

/*PRINTFLIKE3*/
int
zfs_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);

      zfs_verror(hdl, error, fmt, ap);

      va_end(ap);

      return (-1);
}

static int
zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt,
    va_list ap)
{
      switch (error) {
      case EPERM:
      case EACCES:
            zfs_verror(hdl, EZFS_PERM, fmt, ap);
            return (-1);

      case ECANCELED:
            zfs_verror(hdl, EZFS_NODELEGATION, fmt, ap);
            return (-1);

      case EIO:
            zfs_verror(hdl, EZFS_IO, fmt, ap);
            return (-1);

      case EINTR:
            zfs_verror(hdl, EZFS_INTR, fmt, ap);
            return (-1);
      }

      return (0);
}

int
zfs_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
{
      return (zfs_standard_error_fmt(hdl, error, "%s", msg));
}

/*PRINTFLIKE3*/
int
zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);

      if (zfs_common_error(hdl, error, fmt, ap) != 0) {
            va_end(ap);
            return (-1);
      }

      switch (error) {
      case ENXIO:
      case ENODEV:
            zfs_verror(hdl, EZFS_IO, fmt, ap);
            break;

      case ENOENT:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "dataset does not exist"));
            zfs_verror(hdl, EZFS_NOENT, fmt, ap);
            break;

      case ENOSPC:
      case EDQUOT:
            zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
            return (-1);

      case EEXIST:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "dataset already exists"));
            zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
            break;

      case EBUSY:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "dataset is busy"));
            zfs_verror(hdl, EZFS_BUSY, fmt, ap);
            break;
      case EROFS:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "snapshot permissions cannot be modified"));
            zfs_verror(hdl, EZFS_PERMRDONLY, fmt, ap);
            break;
      case ENAMETOOLONG:
            zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
            break;
      case ENOTSUP:
            zfs_verror(hdl, EZFS_BADVERSION, fmt, ap);
            break;
      case EAGAIN:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "pool I/O is currently suspended"));
            zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
            break;
      default:
            zfs_error_aux(hdl, strerror(errno));
            zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
            break;
      }

      va_end(ap);
      return (-1);
}

int
zpool_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
{
      return (zpool_standard_error_fmt(hdl, error, "%s", msg));
}

/*PRINTFLIKE3*/
int
zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
{
      va_list ap;

      va_start(ap, fmt);

      if (zfs_common_error(hdl, error, fmt, ap) != 0) {
            va_end(ap);
            return (-1);
      }

      switch (error) {
      case ENODEV:
            zfs_verror(hdl, EZFS_NODEVICE, fmt, ap);
            break;

      case ENOENT:
            zfs_error_aux(hdl,
                dgettext(TEXT_DOMAIN, "no such pool or dataset"));
            zfs_verror(hdl, EZFS_NOENT, fmt, ap);
            break;

      case EEXIST:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "pool already exists"));
            zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
            break;

      case EBUSY:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool is busy"));
            zfs_verror(hdl, EZFS_BUSY, fmt, ap);
            break;

      case ENXIO:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "one or more devices is currently unavailable"));
            zfs_verror(hdl, EZFS_BADDEV, fmt, ap);
            break;

      case ENAMETOOLONG:
            zfs_verror(hdl, EZFS_DEVOVERFLOW, fmt, ap);
            break;

      case ENOTSUP:
            zfs_verror(hdl, EZFS_POOL_NOTSUP, fmt, ap);
            break;

      case EINVAL:
            zfs_verror(hdl, EZFS_POOL_INVALARG, fmt, ap);
            break;

      case ENOSPC:
      case EDQUOT:
            zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
            return (-1);
      case EAGAIN:
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "pool I/O is currently suspended"));
            zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
            break;

      default:
            zfs_error_aux(hdl, strerror(error));
            zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
      }

      va_end(ap);
      return (-1);
}

/*
 * Display an out of memory error message and abort the current program.
 */
int
no_memory(libzfs_handle_t *hdl)
{
      return (zfs_error(hdl, EZFS_NOMEM, "internal error"));
}

/*
 * A safe form of malloc() which will die if the allocation fails.
 */
void *
zfs_alloc(libzfs_handle_t *hdl, size_t size)
{
      void *data;

      if ((data = calloc(1, size)) == NULL)
            (void) no_memory(hdl);

      return (data);
}

/*
 * A safe form of realloc(), which also zeroes newly allocated space.
 */
void *
zfs_realloc(libzfs_handle_t *hdl, void *ptr, size_t oldsize, size_t newsize)
{
      void *ret;

      if ((ret = realloc(ptr, newsize)) == NULL) {
            (void) no_memory(hdl);
            return (NULL);
      }

      bzero((char *)ret + oldsize, (newsize - oldsize));
      return (ret);
}

/*
 * A safe form of strdup() which will die if the allocation fails.
 */
char *
zfs_strdup(libzfs_handle_t *hdl, const char *str)
{
      char *ret;

      if ((ret = strdup(str)) == NULL)
            (void) no_memory(hdl);

      return (ret);
}

/*
 * Convert a number to an appropriately human-readable output.
 */
void
zfs_nicenum(uint64_t num, char *buf, size_t buflen)
{
      uint64_t n = num;
      int index = 0;
      char u;

      while (n >= 1024) {
            n /= 1024;
            index++;
      }

      u = " KMGTPE"[index];

      if (index == 0) {
            (void) snprintf(buf, buflen, "%llu", (u_longlong_t) n);
      } else if ((num & ((1ULL << 10 * index) - 1)) == 0) {
            /*
             * If this is an even multiple of the base, always display
             * without any decimal precision.
             */
            (void) snprintf(buf, buflen, "%llu%c", (u_longlong_t) n, u);
      } else {
            /*
             * We want to choose a precision that reflects the best choice
             * for fitting in 5 characters.  This can get rather tricky when
             * we have numbers that are very close to an order of magnitude.
             * For example, when displaying 10239 (which is really 9.999K),
             * we want only a single place of precision for 10.0K.  We could
             * develop some complex heuristics for this, but it's much
             * easier just to try each combination in turn.
             */
            int i;
            for (i = 2; i >= 0; i--) {
                  if (snprintf(buf, buflen, "%.*f%c", i,
                      (double)num / (1ULL << 10 * index), u) <= 5)
                        break;
            }
      }
}

void
libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
{
      hdl->libzfs_printerr = printerr;
}

libzfs_handle_t *
libzfs_init(void)
{
      libzfs_handle_t *hdl;

      if ((hdl = calloc(sizeof (libzfs_handle_t), 1)) == NULL) {
            return (NULL);
      }

      /* ZFSFUSE */
      if ((hdl->libzfs_fd = zfsfuse_open(ZFS_SOCK_NAME, O_RDWR)) == -1) {
            free(hdl);
            return (NULL);
      }

      if ((hdl->libzfs_mnttab = setmntent(MNTTAB, "r")) == NULL) {
            (void) close(hdl->libzfs_fd);
            free(hdl);
            return (NULL);
      }

      hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r");

      zfs_prop_init();
      zpool_prop_init();
      libzfs_mnttab_init(hdl);

      return (hdl);
}

void
libzfs_fini(libzfs_handle_t *hdl)
{
      (void) close(hdl->libzfs_fd);
      if (hdl->libzfs_mnttab)
            (void) endmntent(hdl->libzfs_mnttab);
      if (hdl->libzfs_sharetab)
            (void) fclose(hdl->libzfs_sharetab);
      zfs_uninit_libshare(hdl);
      if (hdl->libzfs_log_str)
            (void) free(hdl->libzfs_log_str);
      zpool_free_handles(hdl);
      namespace_clear(hdl);
      libzfs_mnttab_fini(hdl);
      free(hdl);
}

libzfs_handle_t *
zpool_get_handle(zpool_handle_t *zhp)
{
      return (zhp->zpool_hdl);
}

libzfs_handle_t *
zfs_get_handle(zfs_handle_t *zhp)
{
      return (zhp->zfs_hdl);
}

zpool_handle_t *
zfs_get_pool_handle(const zfs_handle_t *zhp)
{
      return (zhp->zpool_hdl);
}

/*
 * Given a name, determine whether or not it's a valid path
 * (starts with '/' or "./").  If so, walk the mnttab trying
 * to match the device number.  If not, treat the path as an
 * fs/vol/snap name.
 */
zfs_handle_t *
zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
{
      struct stat64 statbuf;
      struct extmnttab entry;
      int ret;

      if (path[0] != '/' && strncmp(path, "./", strlen("./")) != 0) {
            /*
             * It's not a valid path, assume it's a name of type 'argtype'.
             */
            return (zfs_open(hdl, path, argtype));
      }

      if (stat64(path, &statbuf) != 0) {
            (void) fprintf(stderr, "%s: %s\n", path, strerror(errno));
            return (NULL);
      }

      rewind(hdl->libzfs_mnttab);
      while ((ret = getextmntent(hdl->libzfs_mnttab, &entry, 0)) == 0) {
            if (makedevice(entry.mnt_major, entry.mnt_minor) ==
                statbuf.st_dev) {
                  break;
            }
      }
      if (ret != 0) {
            return (NULL);
      }

      if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
            (void) fprintf(stderr, gettext("'%s': not a ZFS filesystem\n"),
                path);
            return (NULL);
      }

      return (zfs_open(hdl, entry.mnt_special, ZFS_TYPE_FILESYSTEM));
}

/*
 * Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
 * an ioctl().
 */
int
zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
{
      if (len == 0)
            len = 4096;
      zc->zc_nvlist_dst_size = len;
      if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
          zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == (uint64_t)(uintptr_t) NULL)
            return (-1);

      return (0);
}

/*
 * Called when an ioctl() which returns an nvlist fails with ENOMEM.  This will
 * expand the nvlist to the size specified in 'zc_nvlist_dst_size', which was
 * filled in by the kernel to indicate the actual required size.
 */
int
zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
{
      free((void *)(uintptr_t)zc->zc_nvlist_dst);
      if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
          zfs_alloc(hdl, zc->zc_nvlist_dst_size))
          == (uint64_t)(uintptr_t) NULL)
            return (-1);

      return (0);
}

/*
 * Called to free the src and dst nvlists stored in the command structure.
 */
void
zcmd_free_nvlists(zfs_cmd_t *zc)
{
      free((void *)(uintptr_t)zc->zc_nvlist_conf);
      free((void *)(uintptr_t)zc->zc_nvlist_src);
      free((void *)(uintptr_t)zc->zc_nvlist_dst);
}

static int
zcmd_write_nvlist_com(libzfs_handle_t *hdl, uint64_t *outnv, uint64_t *outlen,
    nvlist_t *nvl)
{
      char *packed;
      size_t len;

      verify(nvlist_size(nvl, &len, NV_ENCODE_NATIVE) == 0);

      if ((packed = zfs_alloc(hdl, len)) == NULL)
            return (-1);

      verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);

      *outnv = (uint64_t)(uintptr_t)packed;
      *outlen = len;

      return (0);
}

int
zcmd_write_conf_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
{
      return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_conf,
          &zc->zc_nvlist_conf_size, nvl));
}

int
zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
{
      return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_src,
          &zc->zc_nvlist_src_size, nvl));
}

/*
 * Unpacks an nvlist from the ZFS ioctl command structure.
 */
int
zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
{
      if (nvlist_unpack((void *)(uintptr_t)zc->zc_nvlist_dst,
          zc->zc_nvlist_dst_size, nvlp, 0) != 0)
            return (no_memory(hdl));

      return (0);
}

int
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
{
      int error;

      zc->zc_history = (uint64_t)(uintptr_t)hdl->libzfs_log_str;
      error = ioctl(hdl->libzfs_fd, request, zc);
      if (hdl->libzfs_log_str) {
            free(hdl->libzfs_log_str);
            hdl->libzfs_log_str = NULL;
      }
      zc->zc_history = 0;

      return (error);
}

/*
 * ================================================================
 * API shared by zfs and zpool property management
 * ================================================================
 */

static void
zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
{
      zprop_list_t *pl = cbp->cb_proplist;
      int i;
      char *title;
      size_t len;

      cbp->cb_first = B_FALSE;
      if (cbp->cb_scripted)
            return;

      /*
       * Start with the length of the column headers.
       */
      cbp->cb_colwidths[GET_COL_NAME] = strlen(dgettext(TEXT_DOMAIN, "NAME"));
      cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(dgettext(TEXT_DOMAIN,
          "PROPERTY"));
      cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN,
          "VALUE"));
      cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN,
          "SOURCE"));

      /* first property is always NAME */
      assert(cbp->cb_proplist->pl_prop ==
          ((type == ZFS_TYPE_POOL) ?  ZPOOL_PROP_NAME : ZFS_PROP_NAME));

      /*
       * Go through and calculate the widths for each column.  For the
       * 'source' column, we kludge it up by taking the worst-case scenario of
       * inheriting from the longest name.  This is acceptable because in the
       * majority of cases 'SOURCE' is the last column displayed, and we don't
       * use the width anyway.  Note that the 'VALUE' column can be oversized,
       * if the name of the property is much longer the any values we find.
       */
      for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
            /*
             * 'PROPERTY' column
             */
            if (pl->pl_prop != ZPROP_INVAL) {
                  const char *propname = (type == ZFS_TYPE_POOL) ?
                      zpool_prop_to_name(pl->pl_prop) :
                      zfs_prop_to_name(pl->pl_prop);

                  len = strlen(propname);
                  if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
                        cbp->cb_colwidths[GET_COL_PROPERTY] = len;
            } else {
                  len = strlen(pl->pl_user_prop);
                  if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
                        cbp->cb_colwidths[GET_COL_PROPERTY] = len;
            }

            /*
             * 'VALUE' column.  The first property is always the 'name'
             * property that was tacked on either by /sbin/zfs's
             * zfs_do_get() or when calling zprop_expand_list(), so we
             * ignore its width.  If the user specified the name property
             * to display, then it will be later in the list in any case.
             */
            if (pl != cbp->cb_proplist &&
                pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
                  cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;

            /*
             * 'NAME' and 'SOURCE' columns
             */
            if (pl->pl_prop == (type == ZFS_TYPE_POOL ? ZPOOL_PROP_NAME :
                ZFS_PROP_NAME) &&
                pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
                  cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
                  cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width +
                      strlen(dgettext(TEXT_DOMAIN, "inherited from"));
            }
      }

      /*
       * Now go through and print the headers.
       */
      for (i = 0; i < 4; i++) {
            switch (cbp->cb_columns[i]) {
            case GET_COL_NAME:
                  title = dgettext(TEXT_DOMAIN, "NAME");
                  break;
            case GET_COL_PROPERTY:
                  title = dgettext(TEXT_DOMAIN, "PROPERTY");
                  break;
            case GET_COL_VALUE:
                  title = dgettext(TEXT_DOMAIN, "VALUE");
                  break;
            case GET_COL_SOURCE:
                  title = dgettext(TEXT_DOMAIN, "SOURCE");
                  break;
            default:
                  title = NULL;
            }

            if (title != NULL) {
                  if (i == 3 || cbp->cb_columns[i + 1] == 0)
                        (void) printf("%s", title);
                  else
                        (void) printf("%-*s  ",
                            cbp->cb_colwidths[cbp->cb_columns[i]],
                            title);
            }
      }
      (void) printf("\n");
}

/*
 * Display a single line of output, according to the settings in the callback
 * structure.
 */
void
zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
    const char *propname, const char *value, zprop_source_t sourcetype,
    const char *source)
{
      int i;
      const char *str;
      char buf[128];

      /*
       * Ignore those source types that the user has chosen to ignore.
       */
      if ((sourcetype & cbp->cb_sources) == 0)
            return;

      if (cbp->cb_first)
            zprop_print_headers(cbp, cbp->cb_type);

      for (i = 0; i < 4; i++) {
            switch (cbp->cb_columns[i]) {
            case GET_COL_NAME:
                  str = name;
                  break;

            case GET_COL_PROPERTY:
                  str = propname;
                  break;

            case GET_COL_VALUE:
                  str = value;
                  break;

            case GET_COL_SOURCE:
                  switch (sourcetype) {
                  case ZPROP_SRC_NONE:
                        str = "-";
                        break;

                  case ZPROP_SRC_DEFAULT:
                        str = "default";
                        break;

                  case ZPROP_SRC_LOCAL:
                        str = "local";
                        break;

                  case ZPROP_SRC_TEMPORARY:
                        str = "temporary";
                        break;

                  case ZPROP_SRC_INHERITED:
                        (void) snprintf(buf, sizeof (buf),
                            "inherited from %s", source);
                        str = buf;
                        break;
                  }
                  break;

            default:
                  continue;
            }

            if (cbp->cb_columns[i + 1] == 0)
                  (void) printf("%s", str);
            else if (cbp->cb_scripted)
                  (void) printf("%s\t", str);
            else
                  (void) printf("%-*s  ",
                      cbp->cb_colwidths[cbp->cb_columns[i]],
                      str);

      }

      (void) printf("\n");
}

/*
 * Given a numeric suffix, convert the value into a number of bits that the
 * resulting value must be shifted.
 */
static int
str2shift(libzfs_handle_t *hdl, const char *buf)
{
      const char *ends = "BKMGTPEZ";
      int i;

      if (buf[0] == '\0')
            return (0);
      for (i = 0; i < strlen(ends); i++) {
            if (toupper(buf[0]) == ends[i])
                  break;
      }
      if (i == strlen(ends)) {
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "invalid numeric suffix '%s'"), buf);
            return (-1);
      }

      /*
       * We want to allow trailing 'b' characters for 'GB' or 'Mb'.  But don't
       * allow 'BB' - that's just weird.
       */
      if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
          toupper(buf[0]) != 'B'))
            return (10*i);

      zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
          "invalid numeric suffix '%s'"), buf);
      return (-1);
}

/*
 * Convert a string of the form '100G' into a real number.  Used when setting
 * properties or creating a volume.  'buf' is used to place an extended error
 * message for the caller to use.
 */
int
zfs_nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
{
      char *end;
      int shift;

      *num = 0;

      /* Check to see if this looks like a number.  */
      if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
            if (hdl)
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "bad numeric value '%s'"), value);
            return (-1);
      }

      /* Rely on stroull() to process the numeric portion.  */
      errno = 0;
      *num = strtoull(value, &end, 10);

      /*
       * Check for ERANGE, which indicates that the value is too large to fit
       * in a 64-bit value.
       */
      if (errno == ERANGE) {
            if (hdl)
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "numeric value is too large"));
            return (-1);
      }

      /*
       * If we have a decimal value, then do the computation with floating
       * point arithmetic.  Otherwise, use standard arithmetic.
       */
      if (*end == '.') {
            double fval = strtod(value, &end);

            if ((shift = str2shift(hdl, end)) == -1)
                  return (-1);

            fval *= pow(2, shift);

            if (fval > UINT64_MAX) {
                  if (hdl)
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "numeric value is too large"));
                  return (-1);
            }

            *num = (uint64_t)fval;
      } else {
            if ((shift = str2shift(hdl, end)) == -1)
                  return (-1);

            /* Check for overflow */
            if (shift >= 64 || (*num << shift) >> shift != *num) {
                  if (hdl)
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "numeric value is too large"));
                  return (-1);
            }

            *num <<= shift;
      }

      return (0);
}

/*
 * Given a propname=value nvpair to set, parse any numeric properties
 * (index, boolean, etc) if they are specified as strings and add the
 * resulting nvpair to the returned nvlist.
 *
 * At the DSL layer, all properties are either 64-bit numbers or strings.
 * We want the user to be able to ignore this fact and specify properties
 * as native values (numbers, for example) or as strings (to simplify
 * command line utilities).  This also handles converting index types
 * (compression, checksum, etc) from strings to their on-disk index.
 */
int
zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
    zfs_type_t type, nvlist_t *ret, char **svalp, uint64_t *ivalp,
    const char *errbuf)
{
      data_type_t datatype = nvpair_type(elem);
      zprop_type_t proptype;
      const char *propname;
      char *value;
      boolean_t isnone = B_FALSE;

      if (type == ZFS_TYPE_POOL) {
            proptype = zpool_prop_get_type(prop);
            propname = zpool_prop_to_name(prop);
      } else {
            proptype = zfs_prop_get_type(prop);
            propname = zfs_prop_to_name(prop);
      }

      /*
       * Convert any properties to the internal DSL value types.
       */
      *svalp = NULL;
      *ivalp = 0;

      switch (proptype) {
      case PROP_TYPE_STRING:
            if (datatype != DATA_TYPE_STRING) {
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "'%s' must be a string"), nvpair_name(elem));
                  goto error;
            }
            (void) nvpair_value_string(elem, svalp);
            if (strlen(*svalp) >= ZFS_MAXPROPLEN) {
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "'%s' is too long"), nvpair_name(elem));
                  goto error;
            }
            break;

      case PROP_TYPE_NUMBER:
            if (datatype == DATA_TYPE_STRING) {
                  (void) nvpair_value_string(elem, &value);
                  if (strcmp(value, "none") == 0) {
                        isnone = B_TRUE;
                  } else if (zfs_nicestrtonum(hdl, value, ivalp)
                      != 0) {
                        goto error;
                  }
            } else if (datatype == DATA_TYPE_UINT64) {
                  (void) nvpair_value_uint64(elem, ivalp);
            } else {
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "'%s' must be a number"), nvpair_name(elem));
                  goto error;
            }

            /*
             * Quota special: force 'none' and don't allow 0.
             */
            if ((type & ZFS_TYPE_DATASET) && *ivalp == 0 && !isnone &&
                (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_REFQUOTA)) {
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "use 'none' to disable quota/refquota"));
                  goto error;
            }
            break;

      case PROP_TYPE_INDEX:
            if (datatype != DATA_TYPE_STRING) {
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "'%s' must be a string"), nvpair_name(elem));
                  goto error;
            }

            (void) nvpair_value_string(elem, &value);

            if (zprop_string_to_index(prop, value, ivalp, type) != 0) {
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "'%s' must be one of '%s'"), propname,
                      zprop_values(prop, type));
                  goto error;
            }
            break;

      default:
            abort();
      }

      /*
       * Add the result to our return set of properties.
       */
      if (*svalp != NULL) {
            if (nvlist_add_string(ret, propname, *svalp) != 0) {
                  (void) no_memory(hdl);
                  return (-1);
            }
      } else {
            if (nvlist_add_uint64(ret, propname, *ivalp) != 0) {
                  (void) no_memory(hdl);
                  return (-1);
            }
      }

      return (0);
error:
      (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
      return (-1);
}

static int
addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
    zfs_type_t type)
{
      int prop;
      zprop_list_t *entry;

      prop = zprop_name_to_prop(propname, type);

      if (prop != ZPROP_INVAL && !zprop_valid_for_type(prop, type))
            prop = ZPROP_INVAL;

      /*
       * When no property table entry can be found, return failure if
       * this is a pool property or if this isn't a user-defined
       * dataset property,
       */
      if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
          (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "invalid property '%s'"), propname);
            return (zfs_error(hdl, EZFS_BADPROP,
                dgettext(TEXT_DOMAIN, "bad property list")));
      }

      if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
            return (-1);

      entry->pl_prop = prop;
      if (prop == ZPROP_INVAL) {
            if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) == NULL) {
                  free(entry);
                  return (-1);
            }
            entry->pl_width = strlen(propname);
      } else {
            entry->pl_width = zprop_width(prop, &entry->pl_fixed,
                type);
      }

      *listp = entry;

      return (0);
}

/*
 * Given a comma-separated list of properties, construct a property list
 * containing both user-defined and native properties.  This function will
 * return a NULL list if 'all' is specified, which can later be expanded
 * by zprop_expand_list().
 */
int
zprop_get_list(libzfs_handle_t *hdl, char *props, zprop_list_t **listp,
    zfs_type_t type)
{
      *listp = NULL;

      /*
       * If 'all' is specified, return a NULL list.
       */
      if (strcmp(props, "all") == 0)
            return (0);

      /*
       * If no props were specified, return an error.
       */
      if (props[0] == '\0') {
            zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                "no properties specified"));
            return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
                "bad property list")));
      }

      /*
       * It would be nice to use getsubopt() here, but the inclusion of column
       * aliases makes this more effort than it's worth.
       */
      while (*props != '\0') {
            size_t len;
            char *p;
            char c;

            if ((p = strchr(props, ',')) == NULL) {
                  len = strlen(props);
                  p = props + len;
            } else {
                  len = p - props;
            }

            /*
             * Check for empty options.
             */
            if (len == 0) {
                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                      "empty property name"));
                  return (zfs_error(hdl, EZFS_BADPROP,
                      dgettext(TEXT_DOMAIN, "bad property list")));
            }

            /*
             * Check all regular property names.
             */
            c = props[len];
            props[len] = '\0';

            if (strcmp(props, "space") == 0) {
                  static char *spaceprops[] = {
                        "name", "avail", "used", "usedbysnapshots",
                        "usedbydataset", "usedbyrefreservation",
                        "usedbychildren", NULL
                  };
                  int i;

                  for (i = 0; spaceprops[i]; i++) {
                        if (addlist(hdl, spaceprops[i], listp, type))
                              return (-1);
                        listp = &(*listp)->pl_next;
                  }
            } else {
                  if (addlist(hdl, props, listp, type))
                        return (-1);
                  listp = &(*listp)->pl_next;
            }

            props = p;
            if (c == ',')
                  props++;
      }

      return (0);
}

void
zprop_free_list(zprop_list_t *pl)
{
      zprop_list_t *next;

      while (pl != NULL) {
            next = pl->pl_next;
            free(pl->pl_user_prop);
            free(pl);
            pl = next;
      }
}

typedef struct expand_data {
      zprop_list_t      **last;
      libzfs_handle_t   *hdl;
      zfs_type_t type;
} expand_data_t;

int
zprop_expand_list_cb(int prop, void *cb)
{
      zprop_list_t *entry;
      expand_data_t *edp = cb;

      if ((entry = zfs_alloc(edp->hdl, sizeof (zprop_list_t))) == NULL)
            return (ZPROP_INVAL);

      entry->pl_prop = prop;
      entry->pl_width = zprop_width(prop, &entry->pl_fixed, edp->type);
      entry->pl_all = B_TRUE;

      *(edp->last) = entry;
      edp->last = &entry->pl_next;

      return (ZPROP_CONT);
}

int
zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp, zfs_type_t type)
{
      zprop_list_t *entry;
      zprop_list_t **last;
      expand_data_t exp;

      if (*plp == NULL) {
            /*
             * If this is the very first time we've been called for an 'all'
             * specification, expand the list to include all native
             * properties.
             */
            last = plp;

            exp.last = last;
            exp.hdl = hdl;
            exp.type = type;

            if (zprop_iter_common(zprop_expand_list_cb, &exp, B_FALSE,
                B_FALSE, type) == ZPROP_INVAL)
                  return (-1);

            /*
             * Add 'name' to the beginning of the list, which is handled
             * specially.
             */
            if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
                  return (-1);

            entry->pl_prop = (type == ZFS_TYPE_POOL) ?  ZPOOL_PROP_NAME :
                ZFS_PROP_NAME;
            entry->pl_width = zprop_width(entry->pl_prop,
                &entry->pl_fixed, type);
            entry->pl_all = B_TRUE;
            entry->pl_next = *plp;
            *plp = entry;
      }
      return (0);
}

int
zprop_iter(zprop_func func, void *cb, boolean_t show_all, boolean_t ordered,
    zfs_type_t type)
{
      return (zprop_iter_common(func, cb, show_all, ordered, type));
}

Generated by  Doxygen 1.6.0   Back to index