ndmpd_zfs.c revision f3012b5938cc7fcdee754ac9bb2817f86ebd9e92
/*
*/
/*
* BSD 3 Clause License
*
* Copyright (c) 2007, The Storage Networking Industry Association.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* - Neither the name of The Storage Networking Industry Association (SNIA)
* nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Copyright (c) 2007, The Storage Networking Industry Association. */
/* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <libzfs.h>
#include <stdio.h>
#include "ndmpd_common.h"
#include "ndmpd.h"
typedef struct {
static int ndmpd_zfs_open_fds(ndmpd_zfs_args_t *);
static void ndmpd_zfs_close_fds(ndmpd_zfs_args_t *);
static void ndmpd_zfs_close_one_fd(ndmpd_zfs_args_t *, int);
static int ndmpd_zfs_header_write(ndmpd_session_t *);
static int ndmpd_zfs_header_read(ndmpd_zfs_args_t *);
static int ndmpd_zfs_backup_send_read(ndmpd_zfs_args_t *);
static int ndmpd_zfs_backup_tape_write(ndmpd_zfs_args_t *);
static int ndmpd_zfs_restore(ndmpd_zfs_args_t *);
static int ndmpd_zfs_restore_tape_read(ndmpd_zfs_args_t *);
static int ndmpd_zfs_restore_recv_write(ndmpd_zfs_args_t *);
static int ndmpd_zfs_reader_writer(ndmpd_zfs_args_t *, int **, int **);
static int ndmpd_zfs_backup_getpath(ndmpd_zfs_args_t *, char *, int);
static int ndmpd_zfs_backup_getenv(ndmpd_zfs_args_t *);
static int ndmpd_zfs_restore_getpath(ndmpd_zfs_args_t *);
static int ndmpd_zfs_restore_getenv(ndmpd_zfs_args_t *);
static int ndmpd_zfs_getenv(ndmpd_zfs_args_t *);
static int ndmpd_zfs_getenv_zfs_mode(ndmpd_zfs_args_t *);
static int ndmpd_zfs_getenv_zfs_force(ndmpd_zfs_args_t *);
static int ndmpd_zfs_getenv_level(ndmpd_zfs_args_t *);
static int ndmpd_zfs_getenv_update(ndmpd_zfs_args_t *);
static int ndmpd_zfs_getenv_dmp_name(ndmpd_zfs_args_t *);
static int ndmpd_zfs_getenv_zfs_backup_size(ndmpd_zfs_args_t *);
static int ndmpd_zfs_send_fhist(ndmpd_zfs_args_t *);
static int ndmpd_zfs_snapshot_prepare(ndmpd_zfs_args_t *);
static int ndmpd_zfs_snapshot_cleanup(ndmpd_zfs_args_t *, int);
static int ndmpd_zfs_snapshot_create(ndmpd_zfs_args_t *);
static int ndmpd_zfs_snapshot_unuse(ndmpd_zfs_args_t *,
static boolean_t ndmpd_zfs_snapshot_ndmpd_generated(char *);
static int ndmpd_zfs_snapshot_prop_find(zfs_handle_t *, void *);
static int ndmpd_zfs_snapshot_prop_get(zfs_handle_t *, char *);
static int ndmpd_zfs_snapshot_prop_add(ndmpd_zfs_args_t *);
static int ndmpd_zfs_snapshot_prop_create(ndmpd_zfs_args_t *, char *,
boolean_t *);
static int ndmpd_zfs_prop_create_subprop(ndmpd_zfs_args_t *, char *, int,
static int ndmpd_zfs_snapshot_prop_remove(ndmpd_zfs_args_t *,
static int ndmpd_zfs_snapname_create(ndmpd_zfs_args_t *, char *, int);
static void ndmpd_zfs_zerr_dma_log(ndmpd_zfs_args_t *);
static int ndmpd_zfs_backup(ndmpd_zfs_args_t *);
#define snapshot_create chkpnt_backup_prepare
/*
* Syntax for com.sun.ndmp:incr property value:
* #.#.n|u/$LEVEL.$DMP_NAME.$ZFS_MODE(/ ...)
*
* where
* #.# is the version number
* 'n' means ndmp-generated; 'u' means user-supplied
* $LEVEL: backup (incremental) level [0-9]
* $DMP_NAME: set name [default: "level"]
* $ZFS_MODE: d | r | p [for dataset, recursive, or package]
*
* Examples:
*
* 0.0.n/0.bob.p
* 0.0.u/1.bob.p/0.jane.d
*
* Note: NDMPD_ZFS_SUBPROP_MAX is calculated based on ZFS_MAXPROPLEN
*/
#define NDMPD_ZFS_PROP_INCR "com.sun.ndmp:incr"
#define NDMPD_ZFS_SUBPROP_MAX 28
/*
* NDMPD_ZFS_LOG_ZERR
*
* As coded, there should be no races in the retrieval of the ZFS errno
* from the ndmpd_zfs_args->nz_zlibh. I.e., for a given ndmpd_zfs backup
* or restore, there should only ever be one ZFS library call taking place
* at any one moment in time.
*/
#define NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, ...) { \
}
int
{
return (-1);
}
return (-1);
}
if (ndmpd_zfs_open_fds(ndmpd_zfs_args) < 0) {
return (-1);
}
ndmpd_zfs_params->mp_seek_func = 0;
switch (version) {
case NDMPV3:
break;
case NDMPV4:
break;
default:
/* error already returned above for this case */
break;
}
return (0);
}
void
{
}
static int
{
int err;
if (err)
return (err);
}
/*
* ndmpd_zfs_close_fds()
*
* In the abort case, use dup2() to redirect the end of the pipe that is
* being written to (to a new pipe). Close the ends of the new pipe to cause
* EPIPE to be returned to the writing thread. This will cause the writer
* and reader to terminate without having any of the writer's data erroneously
* go to any reopened descriptor.
*/
static void
{
int pipe_end;
int fds[2];
return;
}
(void) mutex_lock(&ndmpd_zfs_fd_lock);
} else {
}
(void) mutex_unlock(&ndmpd_zfs_fd_lock);
return;
}
}
(void) mutex_unlock(&ndmpd_zfs_fd_lock);
}
static void
{
(void) mutex_lock(&ndmpd_zfs_fd_lock);
(void) mutex_unlock(&ndmpd_zfs_fd_lock);
}
static int
{
char *buf;
return (-1);
}
sizeof (NDMPUTF8MAGIC));
bufsize);
return (-1);
}
return (0);
}
static int
{
int err;
char *buf;
return (-1);
}
/*
* Read nz_bufsize worth of bytes first (the size of a mover record).
*/
if (err != 0) {
return (-1);
}
"bad magic string\n");
goto _err;
}
"major number larger than supported: (%d %d)\n",
goto _err;
}
/*
* Major version 0 (regardless of minor version):
* Header must be a multiple of the mover record size.
*/
while (header_left > 0) {
if (err == -1) {
NDMP_LOG_ERROR, "bad header\n");
goto _err;
}
header_left -= bufsize;
}
}
return (0);
_err:
return (-1);
}
int
ndmpd_zfs_backup_starter(void *arg)
{
int cleanup_err = 0;
int err = 0;
if (ndmpd_zfs_snapshot_prepare(ndmpd_zfs_args) != 0) {
err = -1;
goto _done;
}
"data bytes_total(including header):%llu",
if (err == 0)
return (err);
}
static int
{
char *envp;
if (!envp)
return (0);
"HIST is not set. No file history will be "
"generated.\n");
return (0);
}
/* Build up a sample root dir stat */
ROOT_INODE) != 0)
return (-1);
ROOT_INODE) != 0)
return (-1);
return (-1);
return (0);
}
static int
{
int result = 0;
int err;
return (-1);
if (ndmpd_zfs_header_write(session)) {
"ndmpd_zfs header write error\n");
return (-1);
}
}
(void) ndmpd_zfs_post_backup(ndmpd_zfs_args);
err = -1;
} else {
err = -1;
if (err == 0) {
} else {
}
}
return (err);
}
/*
* ndmpd_zfs_backup_send_read()
*
* This routine executes zfs_send() to create the backup data stream.
* The value of ZFS_MODE determines the type of zfs_send():
* dataset ('d'): Only the dataset specified (i.e., top level) is backed up
* recursive ('r'): The dataset and its child file systems are backed up
* package ('p'): Same as 'r', except all intermediate snapshots are also
* backed up
*
* Volumes do not have descednants, so 'd' and 'r' produce equivalent results.
*/
static int
{
sendflags_t flags = { 0 };
int err;
if (!zhp) {
return (-1);
}
switch (ndmpd_zfs_args->nz_zfs_mode) {
case ('d'):
break;
case ('r'):
break;
case ('p'):
break;
default:
return (-1);
}
return (-1);
}
}
return (err);
}
/*
* ndmpd_zfs_backup_tape_write()
*
* The data begins on a mover record boundary (because
* the header is the size of a mover record--i.e.
* ndmpd_zfs_args->nz_bufsize).
*/
static int
{
int count;
char *buf;
return (-1);
}
for (;;) {
bufsize);
if (count == 0) /* EOF */ {
"zfs_send stream size: %llu bytes; "
"full backup size (including header): %llu",
*bytes_totalp));
}
if (count == -1) {
errno);
return (-1);
}
(void) ndmpd_zfs_abort((void *) ndmpd_zfs_args);
return (-1);
}
*bytes_totalp += count;
}
/* NOTREACHED */
}
static int
{
char zfs_backup_size[32];
int err;
if (err) {
return (-1);
}
return (0);
}
int
ndmpd_zfs_restore_starter(void *arg)
{
int err;
return (err);
}
static int
{
int result = 0;
int err;
if (ndmpd_zfs_header_read(ndmpd_zfs_args)) {
"ndmpd_zfs header read error\n");
return (-1);
}
}
(void) ndmpd_zfs_post_restore(ndmpd_zfs_args);
err = -1;
} else {
err = -1;
if (err == 0) {
} else {
}
}
return (err);
}
static int
{
char *buf;
int count;
int err;
return (-1);
}
while (*bytes_totalp < backup_size) {
if (err != 0) {
err);
return (-1);
}
bytes);
if (count == -1) {
errno);
}
return (-1);
}
*bytes_totalp += count;
}
return (0);
}
/*
* ndmpd_zfs_restore_recv_write()
*
* This routine executes zfs_receive() to restore the backup.
*/
static int
{
int err;
if (ndmpd_zfs_args->nz_zfs_force)
return (err);
}
/*
* ndmpd_zfs_reader_writer()
*
* Two separate threads are used for actual backup or restore.
*/
static int
int **sendrecv_errp, int **tape_errp)
{
int sendrecv_err;
int tape_err;
switch (ndmpd_zfs_params->mp_operation) {
case NDMP_DATA_OP_BACKUP:
break;
case NDMP_DATA_OP_RECOVER:
break;
}
if (sendrecv_err == 0) {
if (tape_err) {
/*
* The close of the tape side of the pipe will cause
* call and to return. Hence we do not need
* to explicitly cancel the sendrecv_thread here
* (the pthread_join() below is sufficient).
*/
"aborting z-op");
}
(void **) sendrecv_errp);
}
if ((tape_err == 0) &&
if ((sendrecv_err == 0) && (tape_err == 0)) {
(void **) tape_errp);
}
if ((tape_err == 0) &&
}
int
ndmpd_zfs_abort(void *arg)
{
char str[8];
else
str);
return (0);
}
/*
* ndmpd_zfs_pre_backup()
*
* Note: The memset to 0 of nctxp ensures that nctx->nc_cmds == NULL.
* This ensures that ndmp_include_zfs() will fail, which is
* a requirement for "zfs"-type backup.
*/
int
{
int err;
return (0);
if (err != 0) {
(void) ndmpd_zfs_post_backup(ndmpd_zfs_args);
}
return (err);
}
int
{
int err = 0;
return (0);
if (err == -1)
return (err);
}
int
{
char bkpath[ZFS_MAXNAMELEN];
int err;
return (0);
if (err != 0) {
return (-1);
}
if (err != 0) {
return (-1);
}
if (err != 0) {
return (-1);
}
return (0);
}
int
{
int err = 0;
return (0);
if (err == -1)
return (err);
}
{
if (ndmpd_zfs_backup_getenv(ndmpd_zfs_args) != 0)
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
"Snapshot for level %d does not exist\n",
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* ndmpd_zfs_backup_pathvalid()
*
* Make sure the path is of an existing dataset
*/
static boolean_t
{
char zpath[ZFS_MAXNAMELEN];
char propstr[ZFS_MAXPROPLEN];
zfs_type_t ztype = 0;
int err;
!= 0)
return (B_FALSE);
if (!zhp) {
"zfs_open (snap)");
return (B_FALSE);
}
if (err) {
"ndmpd_zfs_snapshot_prop_get failed");
return (-1);
}
"cannot use an ndmpd-generated snapshot\n");
return (B_FALSE);
}
}
if (zhp) {
}
if ((ztype == ZFS_TYPE_VOLUME) ||
(ztype == ZFS_TYPE_FILESYSTEM)) {
return (B_TRUE);
}
"Invalid file system or volume.\n");
return (B_FALSE);
}
/*
* ndmpd_zfs_backup_getpath()
*
* Retrieve the backup path from the environment, which should
* be of the form "/dataset[@snap]". The leading slash is required
* by certain DMA's but can otherwise be ignored.
*
* (Note: "dataset" can consist of more than one component,
*
* The dataset name and the snapshot name (if any) will be
* stored in ndmpd_zfs_args.
*/
static int
int zlen)
{
char *env_path;
char *at;
return (-1);
if (env_path[0] != '/') {
"Invalid path: %s (leading slash required)\n", env_path);
return (-1);
}
if (at) {
*at = '\0';
} else {
}
return (0);
}
static int
{
return (ndmpd_zfs_getenv(ndmpd_zfs_args));
}
{
if (ndmpd_zfs_restore_getenv(ndmpd_zfs_args) != 0)
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
static boolean_t
{
char *at;
if (ndmpd_zfs_restore_getpath(ndmpd_zfs_args) != 0)
return (B_FALSE);
if (at) {
"%s ignored in restore path\n", at);
*at = '\0';
}
if (zhp) {
if (!ndmpd_zfs_is_incremental(ndmpd_zfs_args)) {
"Restore dataset exists.\n"
"A nonexistent dataset must be specified "
"for 'zfs' non-incremental restore.\n");
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* ndmpd_zfs_restore_getpath()
*
* Be sure to not include the leading slash, which is required for
* compatibility with backup applications (NBU) but which is not part
* of the ZFS syntax. (Note that this done explicitly in all paths
* below except those calling ndmpd_zfs_backup_getpath(), because it is
* already stripped in that function.)
*
* In addition, the DMA might add a trailing slash to the path.
* Strip all such slashes.
*/
static int
{
char zpath[ZFS_MAXNAMELEN];
char *nm;
char *p;
int len;
int err;
return (-1);
}
"Invalid path: %s (leading slash required)\n",
return (-1);
}
} else {
/*
* The following does not apply for V4.
*
* Find the last component of nm3_opath.
* nm3_opath has no trailing '/'.
*/
}
}
} else {
if (err)
return (err);
}
len--;
}
return (0);
}
static int
{
if (ndmpd_zfs_getenv_zfs_backup_size(ndmpd_zfs_args) != 0)
return (-1);
return (ndmpd_zfs_getenv(ndmpd_zfs_args));
}
static int
{
if (ndmpd_zfs_getenv_level(ndmpd_zfs_args) != 0)
return (-1);
if (ndmpd_zfs_getenv_zfs_mode(ndmpd_zfs_args) != 0)
return (-1);
if (ndmpd_zfs_getenv_zfs_force(ndmpd_zfs_args) != 0)
return (-1);
if (ndmpd_zfs_getenv_update(ndmpd_zfs_args) != 0)
return (-1);
if (ndmpd_zfs_getenv_dmp_name(ndmpd_zfs_args) != 0)
return (-1);
return (0);
}
static int
{
char *envp;
"defaulting to recursive");
return (0);
}
} else {
"Invalid ZFS_MODE value \"%s\".\n", envp);
return (-1);
}
return (0);
}
/*
* ndmpd_zfs_getenv_zfs_force()
*
* If SMF property zfs-force-override is set to "yes" or "no", this
* value will override any value of NDMP environment variable ZFS_FORCE
* as set by the DMA admin (or override the default of 'n', if ZFS_FORCE
* is not set). By default, zfs-force-override is "off", which means it
* will not override ZFS_FORCE.
*/
static int
{
char *envp_force;
char *override;
"SMF property zfs-force-override set to 'yes', "
"overriding ZFS_FORCE");
return (0);
}
"SMF property zfs-force-override set to 'no', "
"overriding ZFS_FORCE");
return (0);
}
"SMF property zfs-force-override set to invalid value of "
"'%s'; treating it as 'off'.", override);
}
if (envp_force == NULL) {
"env(ZFS_FORCE) not specified, defaulting to FALSE");
return (0);
}
/*
* The value can be either 't' ("true" for v3) or 'y' ("yes" for v4).
*/
return (0);
}
static int
{
char *envp;
"defaulting to 0");
ndmpd_zfs_args->nz_level = 0;
return (0);
}
"Invalid backup level \"%s\".\n", envp);
return (-1);
}
"Invalid backup level \"%s\".\n", envp);
return (-1);
}
return (0);
}
static int
{
char *envp_update;
if (envp_update == NULL) {
"env(UPDATE) not specified, defaulting to TRUE");
return (0);
}
/*
* The value can be either 't' ("true" for v3) or 'y' ("yes" for v4).
*/
return (0);
}
static int
{
char *envp;
"env(DMP_NAME) not specified, defaulting to 'level'");
return (0);
}
return (-1);
return (0);
}
static int
{
char *zfs_backup_size;
if (zfs_backup_size == NULL) {
return (-1);
}
return (0);
}
/*
* ndmpd_zfs_dmp_name_valid()
*
* This function verifies that the dmp_name is valid.
*
* The dmp_name is restricted to alphanumeric characters plus
* the underscore and hyphen, and must be 31 characters or less.
* This is due to its use in the NDMPD_ZFS_PROP_INCR property
* and in the ZFS snapshot name (if an ndmpd-generated snapshot
* is required).
*/
static boolean_t
{
char *c;
"DMP_NAME %s is longer than %d\n",
return (B_FALSE);
}
for (c = dmp_name; *c != '\0'; c++) {
(*c != '_') && (*c != '-')) {
"DMP_NAME %s contains illegal character %c\n",
dmp_name, *c);
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* ndmpd_zfs_is_incremental()
*
* This can only be called after ndmpd_zfs_getenv_level()
* has been called.
*/
static boolean_t
{
return (ndmpd_zfs_args->nz_level != 0);
}
/*
* ndmpd_zfs_snapshot_prepare()
*
* If no snapshot was supplied by the user, create a snapshot
* for use by ndmpd.
*/
static int
{
int zfs_err = 0;
return (-1);
}
if (ndmpd_zfs_snapshot_create(ndmpd_zfs_args) != 0) {
"Error creating snapshot for %s\n",
return (-1);
}
}
"ndmpd_zfs_snapshot_prop_add error\n");
if (ndmpd_zfs_args->nz_ndmpd_snap) {
}
return (-1);
}
return (0);
}
/*
* ndmpd_zfs_snapshot_cleanup()
*
* If UPDATE = y, find the old snapshot (if any) corresponding to
* {LEVEL, DMP_NAME, ZFS_MODE}. If it was ndmpd-generated,
* remove the snapshot. Otherwise, update its NDMPD_ZFS_PROP_INCR
* property to remove {L, D, Z}.
*
* If UPDATE = n, if an ndmpd-generated snapshot was used for backup,
* remove the snapshot. Otherwise, update its NDMPD_ZFS_PROP_INCR
* property to remove {L, D, Z}.
*/
static int
{
/*
* Find the existing snapshot, if any, to "unuse."
* Indicate that the current snapshot used for backup
* should be skipped in the search. (The search is
* sorted by creation time but this cannot be relied
* upon for user-supplied snapshots.)
*/
goto _remove_tmp_snap;
}
ndmpd_generated, &snapdata) != 0) {
"ndmpd_zfs_snapshot_unuse error\n");
goto _remove_tmp_snap;
}
}
goto _remove_tmp_snap;
return (0);
}
return (-1);
}
if (!ndmpd_zfs_args->nz_update)
return (0);
return (-1);
}
static int
{
return (-1);
}
return (-1);
}
return (0);
}
/*
* ndmpd_zfs_snapshot_unuse()
*
* Given a pre-existing snapshot of the given {L, D, Z}:
* If snapshot is ndmpd-generated, remove snapshot.
* If not ndmpd-generated, or if the ndmpd-generated snapshot
* cannot be destroyed, remove the {L, D, Z} substring from the
* snapshot's NDMPD_ZFS_PROP_INCR property.
*
* In the event of a failure, it may be that two snapshots will
* have the {L, D, Z} property set on them. This is not desirable,
* so return an error and log the failure.
*/
static int
{
int zfs_err = 0;
int err = 0;
if (ndmpd_generated) {
if (err) {
" err: %d; zfs_err: %d",
return (-1);
}
}
if (!ndmpd_generated || zfs_err) {
return (-1);
}
return (0);
}
static boolean_t
{
char origin;
return (origin == 'n');
}
/*
* ndmpd_zfs_snapshot_find()
*
* Find a snapshot with a particular value for
* the NDMPD_ZFS_PROP_INCR property.
*/
static int
{
int err;
if (!zhp) {
return (-1);
}
snapdata);
if (err) {
err);
"Error iterating snapshots\n");
return (-1);
}
return (0);
}
/*
* ndmpd_zfs_snapshot_prop_find()
*
* Find a snapshot with a particular value for
* NDMPD_ZFS_PROP_INCR. Fill in data for the first one
* found (sorted by creation time). However, skip the
* the snapshot indicated in nzs_snapskip, if any.
*/
static int
{
char propstr[ZFS_MAXPROPLEN];
char *justsnap;
int err = 0;
(char *)zfs_get_name(zhp));
goto _done;
}
if (err) {
goto _done;
}
if (propstr[0] == '\0') {
(char *)zfs_get_name(zhp));
goto _done;
}
(const char *)findprop_plus_slash)) {
goto _done;
}
goto _done;
}
}
return (err);
}
/*
* ndmpd_zfs_snapshot_prop_get()
*
* Retrieve NDMPD_ZFS_PROP_INCR property from snapshot
*/
static int
{
char *strval;
int err;
propstr[0] = '\0';
return (0);
if (err != 0) {
return (0);
return (-1);
}
if (err != 0) {
return (0);
return (-1);
}
return (0);
}
/*
* ndmpd_zfs_snapshot_prop_add()
*
* Update snapshot's NDMPD_ZFS_PROP_INCR property with
* the current LEVEL, DMP_NAME, and ZFS_MODE values
* (add property if it doesn't exist)
*/
static int
{
char fullname[ZFS_MAXNAMELEN];
char propstr[ZFS_MAXPROPLEN];
int err;
if (!zhp) {
return (-1);
}
return (-1);
}
return (-1);
}
if (set) {
if (err) {
err);
propstr);
return (-1);
}
}
return (0);
}
static int
{
char subprop[ZFS_MAXPROPLEN];
char *p = propstr;
int slash_count = 0;
if (propstr[0] == '\0') {
subprop);
return (0);
}
subprop);
return (0);
}
while (*p) {
if (*(p++) == '/')
slash_count++;
}
if (slash_count >= NDMPD_ZFS_SUBPROP_MAX) {
"snapshot %s: user property %s limit of %d subprops "
"reached; cannot complete operation",
return (-1);
}
return (0);
}
static int
{
}
/*
* ndmpd_zfs_snapshot_prop_remove()
*
* Remove specified substring from the snapshot's
* NDMPD_ZFS_PROP_INCR property
*/
static int
{
char fullname[ZFS_MAXNAMELEN];
char newprop[ZFS_MAXPROPLEN];
char tmpstr[ZFS_MAXPROPLEN];
char *ptr;
int err;
if (!zhp) {
return (-1);
}
/*
* If the substring to be removed is the only {L, D, Z}
* in the property, remove the entire property
*/
tmpstr[0] = '\0';
if (err) {
err);
return (-1);
}
return (0);
}
(const char *)findprop_plus_slash);
/*
* This shouldn't happen. Just return success.
*/
return (0);
}
/*
* Remove "nzs_findprop" substring from property
*
* Example property:
* 0.0.u/1.bob.p/0.jane.d
*
* Note that there will always be a prefix to the
* strstr() result. Hence the below code works for
* all cases.
*/
ptr--;
if (err) {
return (-1);
}
return (0);
}
static boolean_t
{
if (*major > NDMPD_ZFS_PROP_MAJOR_VERSION) {
return (B_FALSE);
}
if (*minor > NDMPD_ZFS_PROP_MINOR_VERSION) {
}
"supported prop major (%u <= %u); (snapprop minor: %u [%u])",
return (B_TRUE);
}
static int
{
char subprop[ZFS_MAXPROPLEN];
int err = 0;
"ndmp.%s.%ld.%ld",
return (-1);
}
return (0);
}
static void
{
case EZFS_EXISTS:
case EZFS_BUSY:
case EZFS_NOENT:
case EZFS_INVALIDNAME:
case EZFS_MOUNTFAILED:
case EZFS_UMOUNTFAILED:
case EZFS_NAMETOOLONG:
case EZFS_BADRESTORE:
/* use existing error text */
"%s: %s: %s\n",
break;
case EZFS_NOMEM:
"Unable to obtain memory for operation\n");
break;
case EZFS_PROPSPACE:
"A bad ZFS quota or reservation was encountered.\n");
break;
case EZFS_BADSTREAM:
"The backup stream is invalid.\n");
break;
case EZFS_ZONED:
"An error related to the local zone occurred.\n");
break;
case EZFS_NOSPC:
"No more space is available\n");
break;
case EZFS_IO:
"An I/O error occurred.\n");
break;
default:
"An internal ndmpd error occurred. "
"Please contact support\n");
break;
}
}
void
char *format, ...)
{
static char buf[1024];
/*LINTED variable format specifier */
if ((log_type) == NDMP_LOG_ERROR) {
} else {
}
}