tlm_restore_writer.c revision 42ed7838f131b8f58d6c95db1c7e3a6a3e6ea7e4
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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.
*/
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <time.h>
#include <utime.h>
#include <unistd.h>
#include <pthread.h>
#include <archives.h>
#include <priv.h>
#include <tlm.h>
#include <libzfs.h>
#include <pwd.h>
#include <grp.h>
#include <ndmpd_prop.h>
#include "tlm_proto.h"
typedef boolean_t name_match_fp_t(char *s, char *t);
tlm_acls_t *acls);
static long restore_file(int *fp,
char *real_name,
long size,
tlm_acls_t *,
tlm_cmd_t *,
tlm_job_stats_t *);
static long restore_xattr_hdr(int *fp,
char *name,
char *fname,
long size,
static int get_long_name(int lib,
int drv,
long recsize,
char *name,
long *buf_spot,
static int get_humongus_file_header(int lib,
int drv,
long recsize,
char *name,
tlm_cmd_t *);
static int create_directory(char *dir,
tlm_job_stats_t *);
static int create_hard_link(char *name,
char *link,
tlm_acls_t *,
tlm_job_stats_t *);
static int create_sym_link(char *dst,
char *target,
tlm_acls_t *,
tlm_job_stats_t *);
static int create_fifo(char *name,
tlm_acls_t *);
static long load_acl_info(int lib,
int drv,
long size,
tlm_acls_t *,
long *acl_spot,
tlm_cmd_t *);
static char *get_read_buffer(int want,
int *error,
int *actual_size,
tlm_cmd_t *);
static boolean_t wildcard_enabled(void);
char **sels,
char **exls,
int flags,
int *mchtype,
int *pos);
char *buf,
int pos,
char *path);
char *real_name,
int pos,
char *path);
typedef struct stack_ent {
char *se_name;
} stack_ent_t;
/*
* dtree_push
*/
int
{
int len;
return (-1);
}
return (-1);
}
}
/*
* dtree_pop
*/
int
{
int err;
if (err)
return (-1);
return (err);
}
/*
* dtree_peek
*/
char *
{
int err;
if (err)
return (NULL);
}
/*
* NBU and EBS may not send us the correct file list containing hardlinks
* during a DAR restore, e.g. they appear always send the first name
* associated with an inode, even if other link names were
* selected for the restore. As a workaround, we use the file name entry
* in sels[] (ignore the name in the tar header) as restore target.
*/
static char *
char *longname)
{
int x;
*pos = x;
"to replace hardlink name [%s], pos [%d]",
}
}
return (NULL);
}
/*
* Main dir restore function for tar
*/
int
struct rs_name_maker *rnp,
int lib,
int drv,
char **sels, /* what to get off the tape */
char **exls, /* what to leave behind */
int flags,
{
int fp = 0; /* file being restored ... */
/* ...need to preserve across volume changes */
char *longname;
char *longlink;
char *hugename;
long acl_spot; /* any ACL info on the next volume */
long file_size; /* size of file to restore */
long size_left = 0; /* need this after volume change */
int last_action = 0; /* what we are doing at EOT */
int chk_rv; /* scratch area */
/*
* if an exact match is found for
* restore and its position in the
* selections list
*/
int nzerohdr; /* the number of empty tar headers */
int rv;
char *bkpath;
char *parentlnk;
/*
* The directory where temporary files may be created during a partial
* non-DAR restore of hardlinks. It is intended to be initialized by
* an environment variable that can be set by user.
*
* It is not initialized for now. We keep it here for future use.
*/
char *tmplink_dir = NULL;
int dar_recovered = 0;
/*
* startup
*/
stp = cstack_new();
return (-TLM_NO_SCRATCH_SPACE);
}
acl_spot = 0;
*hugename = '\0';
*parentlnk = '\0';
nm_end = 0;
*longname = '\0';
lnk_end = 0;
*longlink = '\0';
}
/*
* work
*/
rv = 0;
nzerohdr = 0;
char *file_name;
char *link_name;
int erc;
int actual_size;
int want = sizeof (tlm_tar_hdr_t);
/* The inode of an LF_LINK type. */
unsigned long hardlink_inode = 0;
/*
* Indicate whether a file with the same inode has been
* restored.
*/
int hardlink_done = 0;
/* The path of the restored hardlink file */
char *hardlink_target = NULL;
int is_hardlink = 0;
/*
* Whether a temporary file should be created for restoring
* hardlink.
*/
int hardlink_tmp_file = 0;
char *hardlink_tmp_name = ".tmphlrsnondar";
/* used to make up hardlink_tmp_name */
static int hardlink_tmp_idx = 0;
if (break_flg) {
"Exiting writer thread drive %d", drv);
break;
}
if (multi_volume) {
/*
* the previous volume is out of data
* and is back in the rack, a new tape
* is loaded and ready to read.
*
* We need to pick up where we left off.
*/
tar_hdr = &fake_tar_hdr;
last_action = 0;
} else {
rv = -1;
continue;
}
/*
* we can ignore read errors here because
* 1) they are logged by Restore Reader
* 2) we are not doing anything important here
* just looking for the next work record.
*/
if (actual_size < want) {
/*
* EOF hits here
*
* wait for another buffer to come along
* or until the Reader thread tells us
* that no more tapes will be loaded ...
* time to stop.
*/
continue;
}
/*
* check for "we are lost"
*/
if (chk_rv == 0) {
/* one of the end of tar file marks */
if (++nzerohdr >= 2) {
"nzerohdr %d, breaking",
nzerohdr);
/* end of tar file */
break;
}
nzerohdr);
continue;
} else if (chk_rv < 0) {
nzerohdr = 0;
/* skip this record */
continue;
}
nzerohdr = 0;
/*
* When files are spanned to the next tape, the
* information of the acls must not be over-written
* by the information of the LF_MULTIVOL and LF_VOLHDR
* header, whose information is irrelevant to the file.
* The information of the original header must be
* kept in the 'acl'.
*/
}
acl_spot = 0;
}
}
/*
* If the restore is running using DAR we should check for
* extended attribute entries
*/
if (dar_recovered &&
break;
switch (tar_hdr->th_linkflag) {
case LF_MULTIVOL:
multi_volume = TRUE;
break;
case LF_LINK:
is_hardlink = 1;
/*
* Check if we have restored a link with the same inode
* If the inode is 0, we have to restore it as a
* regular file.
*/
if (hardlink_inode) {
hardlink_inode, 0, &hardlink_target);
}
if (hardlink_done) {
"found hardlink, inode = %u, target = [%s]",
/* create a hardlink to hardlink_target */
/*
* This means that DMA did not send us
* the correct fh_info for the file
* in restore list. We use the file
* name entry in sels[] (ignore the
* name in the tar header) as restore
* target.
*/
if (DAR) {
}
} else {
if (!nmp) {
"can't make name for %s",
longname);
}
}
if (nmp) {
if (hardlink_target) {
if (erc == 0) {
(void)
"restored %s -> %s",
nmp,
}
} else {
"no target for hardlink %s",
nmp);
}
name[0] = 0;
}
nm_end = 0;
longname[0] = 0;
lnk_end = 0;
longlink[0] = 0;
break;
}
/* otherwise fall through, restore like a normal file */
/*FALLTHROUGH*/
case LF_OLDNORMAL:
/*
* check for TAR's end-of-tape method
* of zero filled records.
*/
break;
}
/*
* otherwise fall through,
* this is an old style normal file header
*/
/*FALLTHROUGH*/
case LF_NORMAL:
case LF_CONTIG:
if (*hugename != 0) {
} else if (*longname == 0) {
/*
* check for old tar format, it
* does not have a leading "/"
*/
longname[0] = '/';
longname[1] = 0;
} else {
}
}
if (!want_this_file) {
/*
* This means that DMA did not send us valid
* fh_info for the file in restore list. We
* use the file name entry in sels[] (ignore
* the name in the tar header) as restore
* target.
*/
if (nmp) {
} else {
break;
}
}
} else {
if (!nmp)
}
if (nmp)
/*
* For a hardlink, even if it's not asked to be
* restored, we restore it to a temporary location,
* in case other links to the same file need to be
* restored later.
*
* The temp files are created in tmplink_dir, with
* names like ".tmphlrsnondar*". They are cleaned up
* at the completion of a restore. However, if a
* restore were interrupted, e.g. by a system reboot,
* they would have to be cleaned up manually in order
* for the disk space to be freed.
*
* If tmplink_dir is NULL, no temperorary files are
* created during a restore. This may result in some
* hardlinks not being restored during a partial
* restore.
*/
if (tmplink_dir) {
"%s/%s_%d", tmplink_dir,
hardlink_tmp_file = 1;
"To restore temp hardlink file %s.",
nmp);
} else {
"No tmplink_dir specified.");
}
}
/*
* In the case of non-DAR, we have to record the first
* link for an inode that has multiple links. That's
* the only link with data records actually backed up.
* In this way, when we run into the other links, they
* will be treated as links, and we won't go to look
* for the data records to restore. This is not a
* problem for DAR, where DMA tells the tape where
* to locate the data records.
*/
if (is_hardlink && !DAR) {
0, nmp, hardlink_tmp_file))
"failed to add (%u, %s) to HL q",
}
/* remove / reverse the temporary stuff */
if (hardlink_tmp_file) {
hardlink_tmp_file = 0;
}
/*
* Check if it is time to set the attribute
* of the restored directory
*/
break;
}
rv = -1;
longname);
break;
}
if (want_this_file) {
}
if (huge_size < 0) {
huge_size = 0;
}
if (PM_EXACT_OR_CHILD(mchtype)) {
(void) tlm_entry_restored(job_stats,
/*
* Add an entry to hardlink_q to record
* this hardlink.
*/
if (is_hardlink) {
"Restored hardlink file %s",
nmp);
if (DAR) {
(void) hardlink_q_add(
hardlink_inode, 0,
nmp, 0);
}
}
}
nm_end = 0;
longname[0] = 0;
lnk_end = 0;
longlink[0] = 0;
hugename[0] = 0;
name[0] = 0;
}
break;
case LF_XATTR:
break;
case LF_SYMLINK:
if (nmp) {
if (erc == 0 &&
(void) tlm_entry_restored(
name[0] = 0;
}
}
nm_end = 0;
longname[0] = 0;
lnk_end = 0;
longlink[0] = 0;
break;
case LF_DIR:
if (erc == 0 &&
(void) tlm_entry_restored(
/*
* Check if it is time to set
* the attribute of the restored
* directory
*/
!= NULL) {
break;
}
name[0] = 0;
}
}
nm_end = 0;
longname[0] = 0;
lnk_end = 0;
longlink[0] = 0;
break;
case LF_FIFO:
if (nmp) {
if (erc == 0 &&
(void) tlm_entry_restored(
name[0] = 0;
}
}
nm_end = 0;
longname[0] = 0;
lnk_end = 0;
longlink[0] = 0;
break;
case LF_LONGLINK:
if (size_left != 0)
"fsize %d sleft %d lnkend %d",
break;
case LF_LONGNAME:
&nm_end, local_commands);
if (size_left != 0)
"fsize %d sleft %d nmend %d",
is_long_name = TRUE;
break;
case LF_ACL:
break;
case LF_VOLHDR:
break;
case LF_HUMONGUS:
break;
default:
break;
}
/*
* If the restore is running using DAR we should check for
* long file names and HUGE file sizes.
*/
!huge_size && !is_long_name)
dar_recovered = 1;
}
/*
* tear down
*/
if (fp != 0) {
}
;
return (rv);
}
/*
* Main file restore function for tar (should run as a thread)
*/
int
{
char **sels; /* list of files desired */
char **exls; /* list of files not wanted */
char *dir; /* where to restore the files */
/* the restore job name */
int erc; /* error return codes */
int flags;
struct rs_name_maker rn;
flags = 0;
return (-1);
}
/*
* do not test for "dir" having no string, since that
* is a legal condition. Restore to origional location
* will not have a restore directory.
*/
if (*job == '\0') {
return (-1);
}
return (-1);
}
if (wildcard_enabled())
local_commands->tc_ref++;
/*
* let the launcher continue
*/
/*
* work
*/
/*
* teardown
*/
return (erc);
}
/*
* Creates the directories all the way down to the
* end if they dont exist
*/
int
{
char c;
do {
c = *cp;
*cp = '\0';
" creating directory %s",
*cp = c;
return (-1);
}
*cp = c;
}
return (0);
}
/*
* Creates the directories leading to the given path
*/
int
{
int rv;
char *cp;
return (-1);
}
if (cp)
*cp = '\0';
if (rv < 0) /* need new directories */
if (cp)
*cp = '/';
return (rv);
}
/*
* read the file off the tape back onto disk
*/
static long
restore_file(int *fp,
char *real_name,
long size,
{
if (!real_name) {
if (want_this_file) {
}
} else
/*
* OK, some FM is creeping in here ...
* int *fp is used to keep the
* backup file channel open through
* the interruption of EOT and
* processing the headers of the
* next tape. So, if *fp is zero
* then no file is open yet and all
* is normal. If *fp has a number
* then we are returning after an
* EOT break.
*
* *fp is now also open for HUGE files
* that are put back in sections.
*/
if (*fp == 0 && want_this_file) {
int erc_stat;
if (erc_stat < 0) {
/*EMPTY*/
/* new file */
} else if (acls->acl_overwrite) {
/*EMPTY*/
/* take this file no matter what */
} else if (acls->acl_update) {
/*EMPTY*/
/* tape is newer */
} else {
/* disk file is newer */
}
} else {
/*
* no overwrite, no update,
* do not ever replace old files.
*/
}
if (want_this_file) {
if (*fp == -1) {
"Could not open %s for restore.",
/*
* we cannot return here,
* the file is still on
* the tape and must be
* skipped over.
*/
}
}
}
/*
* this is the size left in the next segment
*/
/*
* work
*/
int actual_size;
int error;
char *rec;
int write_size;
/*
* Use bytes_in_file field to tell reader the amount
* of data still need to be read for this file.
*/
error = 0;
if (actual_size <= 0) {
"RESTORE WRITER> error %d, actual_size %d",
error, actual_size);
/* no more data for this file for now */
job_stats->js_bytes_in_file = 0;
return (size);
} else if (error) {
break;
} else {
if (want_this_file) {
}
size -= write_size;
}
}
/* no more data for this file for now */
job_stats->js_bytes_in_file = 0;
/*
* teardown
*/
*fp = 0;
}
return (0);
}
/*
* Set the extended attributes file attribute
*/
static void
{
}
/*
* Read the system attribute file in a single buffer to write
* it as a single write. A partial write to system attribute would
* cause an EINVAL on write.
*/
static char *
{
char *buf, *p;
int read_size;
int len;
if (actual_size > size)
return (rec);
return (NULL);
}
buf += actual_size;
while (actual_size < size) {
actual_size += len;
}
return (rec);
}
/*
* read the extended attribute header and write
* it to the file
*/
static long
restore_xattr_hdr(int *fp,
char *name,
char *fname,
long size,
{
int namelen;
char *xattrname;
int actual_size;
int error;
if (!fname) {
} else {
}
error = 0;
"Could not read xattr [%s:%s] for restore. ",
return (0);
}
/* Check extended attribute header */
return (0);
}
if (*fp == 0) {
int fd;
if (fd == -1) {
"Could not open xattr [%s:%s] for restore err=%d.",
return (0);
}
}
/* Get the actual extended attribute file */
"Could not read xattr data [%s:%s] for restore. ",
return (0);
}
char *rec;
int write_size;
int sysattr_write = 0;
error = 0;
return (size);
}
actual_size = size;
sysattr_write = 1;
}
if (actual_size <= 0) {
"RESTORE WRITER> error %d, actual_size %d",
error, actual_size);
return (size);
} else if (error) {
break;
} else {
if (sysattr_write)
break;
}
size -= write_size;
}
if (sysattr_write)
}
if (*fp != 0) {
*fp = 0;
}
return (0);
}
/*
* Match the name with the list
*/
static int
{
int i;
char *cp;
break;
}
}
return (found);
}
/*
* On error, return FALSE and prevent restoring(probably) unwanted data.
*/
static int
{
char tmp[TLM_MAX_PATH_NAME];
"is_parent> path too long [%s]", parent);
} else
} else {
"is_parent> path too long [%s]", parent);
} else
}
return (rv);
}
/*
* Used to match the filename inside the list
*/
static boolean_t
strexactcmp(char *s, char *t)
{
}
/*
* Check if the file is needed to be restored
*/
static boolean_t
is_file_wanted(char *name,
char **sels,
char **exls,
int flags,
int *mchtype,
int *pos)
{
char *p_sel;
int i;
return (FALSE);
*pos = 0;
/*
* For empty selection, restore everything
*/
return (TRUE);
}
return (FALSE);
}
else
}
/*
* Try exact match.
*/
break;
}
/*
* Try "entry/" and the current selection. The
* current selection may be something like "<something>/".
*/
break;
}
/*
* If the following check returns true it means that the
* 'name' is an entry below the 'p_sel' hierarchy.
*/
break;
}
/*
* There is a special case for parent directories of a
* selection. If 'p_sel' is something like "*d1", the
* middle directories of the final entry can't be determined
* until the final entry matches with 'p_sel'. At that
* time the middle directories of the entry have been passed
* and they can't be restored.
*/
break;
}
}
/* Check for exclusions. */
}
*pos = i;
return (found);
}
/*
* Read the specified amount data into the buffer. Detects EOT or EOF
* during read.
*
* Returns the number of bytes actually read. On error returns -1.
*/
static int
input_mem(int l,
int d,
char *mem,
int len)
{
int err;
char *rec;
return (-1);
}
while (toread > 0) {
if (actual_size <= 0) {
err, actual_size);
break;
} else if (err) {
return (-1);
}
}
}
/*
* pick up the name and size of a HUGE file
*/
static int
int drv,
long recsize,
char *name,
{
int rv;
rv = 0;
if (recsize == 0) {
/*
* The humongus_file_header was written in a
* RECORDSIZE block and the header.size field of this
* record was 0 before this fix. For backward compatiblity
* read only one RECORDSIZE-size block if the header.size
* field is 0. Otherwise the header.size field should show
* the length of the data of this header.
*/
}
rv = -1;
*size = 0;
*name = '\0';
} else {
/*
* Note: Since the backed up names are not longer than
* NAME_MAX and the buffer passed to us is
* TLM_MAX_PATH_NAME, it should be safe to use strlcpy
* without check on the buffer size.
*/
}
return (rv);
}
/*
* pick up the long name from the special tape file
*/
static int
get_long_name(int lib,
int drv,
long recsize,
char *name,
long *buf_spot,
{
int nread;
*buf_spot);
if (*buf_spot < 0)
*buf_spot = 0;
recsize);
if (nread < 0) {
} else {
}
}
/*
* create a new directory
*/
static int
{
char *p;
char temp;
int erc;
/*
* Make sure all directories in this path exist, create them if
* needed.
*/
erc = 0;
p = &dir[1];
do {
temp = *p;
*p = 0;
if (erc < 0) {
"Could not create directory %s",
dir);
break;
}
}
*p = temp;
}
p++;
} while (temp != 0);
return (erc);
}
/*
* create a new hardlink
*/
static int
{
int erc;
name_new);
return (-1);
}
if (erc) {
} else {
}
return (erc);
}
/*
* create a new symlink
*/
/*ARGSUSED*/
static int
{
int erc;
return (-1);
if (erc) {
job_satats->js_errors++;
} else {
}
return (erc);
}
/*
* create a new FIFO
*/
static int
{
return (0);
}
/*
* read in the ACLs for the next file
*/
static long
load_acl_info(int lib,
int drv,
long file_size,
long *acl_spot,
{
char *bp;
int nread;
/*
* If the ACL is spanned on tapes, then the acl_spot should NOT be
* 0 on next calls to this function to read the rest of the ACL
* on next tapes.
*/
if (*acl_spot == 0) {
}
if (nread < 0) {
*acl_spot = 0;
return (0);
}
}
static int
ndmp_set_eprivs_least(void)
{
return (-1);
}
return (-1);
}
return (0);
}
static int
ndmp_set_eprivs_all(void)
{
return (-1);
}
return (-1);
}
return (0);
}
/*
* Set the standard attributes of the file
*/
static void
{
return;
}
}
/*
* Change effective privileges to 'all' which is required to
* change setuid bit for 'root' owned files. If fails, just
* send error to log file and proceed.
*/
if (ndmp_set_eprivs_all()) {
"Could not set effective privileges to 'all'.");
} else {
}
}
" permission for file %s.", name);
}
/*
* Give up the 'all' privileges for effective sets and go back
* to least required privileges. If fails, just send error to
* log file and proceed.
*/
if (ndmp_set_eprivs_least())
"Could not set least required privileges.");
}
}
/*
* Set the ACL info for the file
*/
static void
{
int erc;
if (name)
if (acls != 0) {
/* Need a place to save real modification time */
if (!acls->acl_non_trivial) {
return;
}
if (erc != 0) {
"TAPE RESTORE> acl_fromtext errno %d", erc);
}
if (aclp) {
if (erc < 0) {
"TAPE RESTORE> acl_set errno %d", errno);
}
}
}
}
/*
* a wrapper to tlm_get_read_buffer so that
* we can cleanly detect ABORT commands
* without involving the TLM library with
* our problems.
*/
static char *
get_read_buffer(int want,
int *error,
int *actual_size,
{
char *rec;
if (rec != 0) {
return (rec);
}
}
/*
* the job is ending, give Writer a buffer that will never be read ...
* it does not matter anyhow, we are aborting.
*/
return (NULL);
}
/*
* Enable wildcard for restore options
*/
static boolean_t
wildcard_enabled(void)
{
char *cp;
}
/*
* Concatenate two names
*/
/*ARGSUSED*/
static char *
{
char *rv;
if (!buf) {
} else if (!path) {
} else
return (rv);
}
/*
* Create a new name path for restore
*/
static char *
{
return (NULL);
}
/*
* Iterate over ZFS metadata stored in the backup stream and use the callback
* to restore it.
*/
int
void *), void *ptr)
{
int actual_size;
char plname[100];
int rv, i;
return (-1);
return (-1);
/* Default minimum bytes needed */
size = sizeof (ndmp_metadata_header_t) +
ZFS_MAX_PROPS * sizeof (ndmp_metadata_property_t);
return (-1);
/* LINTED improper alignment */
/* No more metadata */
return (0);
}
== NULL) {
return (-1);
}
}
}
actual_size : size);
sz = actual_size;
}
}
/* LINTED improper alignment */
/* New metadata format */
/* LINTED improper alignment */
/* Major header mismatch */
return (-1);
}
/* Minor header mismatch */
"metadata header mismatch m%d != m%d",
continue;
}
sizeof (plname));
goto nvlist_err;
continue;
if (nvlist_alloc(&valp,
NV_UNIQUE_NAME, 0) != 0 ||
valp) != 0) {
goto nvlist_err;
}
}
} else {
sizeof (plname));
goto nvlist_err;
continue;
if (nvlist_alloc(&valp,
NV_UNIQUE_NAME, 0) != 0 ||
valp) != 0) {
goto nvlist_err;
}
}
}
goto nvlist_err;
}
}
return (0);
}
return (-1);
}
/*
* Returns the version number of the plugin which created the metadata
*/
{
int actual_size;
int rv;
int size;
return (0);
return (0);
size = sizeof (ndmp_metadata_header_t);
/* LINTED improper alignment */
/* No more metadata */
return (0);
}
}
return (nctx->nc_plversion);
}