tlm_backup_reader.c revision 5181c2afafac4c39b4d5d9d8dd14e6ba0d227db2
/*
*/
/*
* 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 <stdio.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#include <pthread.h>
#include <archives.h>
#include <tlm.h>
#include <libzfs.h>
#include <libcmdutils.h>
#include <pwd.h>
#include <grp.h>
#include "tlm_proto.h"
static char *get_write_buffer(long size,
long *actual_size,
tlm_cmd_t *);
static int output_acl_header(sec_attr_t *,
tlm_cmd_t *);
static int output_file_header(char *name,
char *link,
tlm_acls_t *,
int section,
tlm_cmd_t *);
static int output_xattr_header(char *fname,
char *aname,
int fd,
tlm_acls_t *,
int section,
tlm_cmd_t *);
extern libzfs_handle_t *zlibh;
S_ISCHR(a))
/*
* output_mem
*
* Gets a IO write buffer and copies memory to the that.
*/
static void
int len)
{
long actual_size, rec_size;
char *rec;
while (len > 0) {
}
}
/*
* tlm_output_dir
*
* Put the directory information into the output buffers.
*/
int
{
/*
* Send the node or path history of the directory itself.
*/
/* fhdir_cb is handled in ndmpd_tar3.c */
return (0);
}
/*
* tar_putdir
*
* Main dir backup function for tar
*/
int
{
int rv;
}
/*
* output_acl_header
*
* output the ACL header record and data
*/
static int
{
long actual_size;
long acl_size;
return (0);
if (!tar_hdr)
return (0);
acl_size);
0444);
"%011o ", 0);
return (0);
}
/*
* output_humongus_header
*
* output a special header record for HUGE files
* output is: 1) a TAR "HUGE" header redord
* 2) a "file" of size, name
*/
static int
{
char *buf;
int len;
long actual_size;
/*
* buf will contain: "%llu %s":
* - 20 is the maximum length of 'ulong_tlong' decimal notation.
* - The first '1' is for the ' ' between the "%llu" and the fullname.
* - The last '1' is for the null-terminator of fullname.
*/
return (-1);
if (!tar_hdr) {
return (0);
}
len);
return (0);
}
/*
* output_xattr_header
*
* output the TAR header record for extended attributes
*/
static int
{
long actual_size;
int hsize;
int comlen;
int namesz;
if (section_name == NULL)
return (-TLM_NO_SCRATCH_SPACE);
return (-TLM_OPEN_ERR);
}
/*
* if the file has to go out in sections,
* we must mung the name.
*/
if (section == 0) {
} else {
(void) snprintf(section_name,
}
if (!tar_hdr) {
return (0);
}
hsize);
if (!xhdr) {
return (0);
}
sizeof (struct xattr_hdr));
/* No support for links in extended attributes */
return (0);
}
/*
* output_file_header
*
* output the TAR header record
*/
static int
{
static longlong_t file_count = 0;
long actual_size;
char *uname = "";
char *gname = "";
if (section_name == NULL)
return (-TLM_NO_SCRATCH_SPACE);
/*
* if the file has to go out in sections,
* we must mung the name.
*/
if (section == 0) {
} else {
(void) snprintf(section_name,
}
uid = UID_NOBODY;
gid = GID_NOBODY;
/*
* file name is too big, it must go out
* in its own data file
*/
if (!tar_hdr) {
return (0);
}
"%s%08qd.fil",
file_count++);
"%011o ", nmlen);
"%06o ", uid);
"%06o ", gid);
"%.31s", uname);
"%.31s", gname);
(void) output_mem(local_commands,
(void *)section_name, nmlen);
}
/*
* link name is too big, it must go out
* in its own data file
*/
if (!tar_hdr) {
return (0);
}
"%s%08qd.slk",
file_count++);
"%011o ", lnklen);
"%06o ", uid);
"%06o ", gid);
"%.31s", uname);
"%.31s", gname);
lnklen);
}
if (!tar_hdr) {
return (0);
}
if (long_name) {
"%s%08qd.fil",
file_count++);
} else {
}
link);
if (long_link) {
"%s%08qd.slk",
file_count++);
} else {
}
case S_IFDIR:
break;
case S_IFIFO:
break;
case S_IFBLK:
case S_IFCHR:
else
break;
default:
/* mark file with hardlink LF_LINK */
} else {
}
}
uid);
gid);
uname);
gname);
if (file_count > 99999990) {
file_count = 0;
}
}
return (0);
}
/*
* tlm_readlink
*
* Read where the softlink points to. Read the link in the checkpointed
* path if the backup is being done on a checkpointed file system.
*/
static int
{
int len;
/*
* realink(2) doesn't null terminate the link name. We must
* do it here.
*/
} else {
buf[0] = '\0';
/* Backup the link if the destination missing */
return (0);
}
return (len);
}
/*
* 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 *
{
int len;
long write_size;
return (rec);
if (!rec)
return (0);
}
return (rec);
}
/*
* tlm_output_xattr
*
* Put this file into the output buffers.
*/
/*ARGSUSED*/
{
char *fullname; /* directory + name */
char *snapname; /* snapshot name */
int section; /* section of a huge file */
int fd;
int afd = 0;
/* for Multi Volume record */
char *attrname;
char *fnamep;
int rv = 0;
return (TLM_NO_SOURCE_FILE);
}
return (-TLM_NO_SCRATCH_SPACE);
}
return (-TLM_NO_SCRATCH_SPACE);
}
return (0);
}
goto err_out;
}
goto err_out;
}
/*
* Open the file for reading.
*/
if (fd == -1) {
goto err_out;
}
section = 0;
goto err_out;
}
int section_size;
continue;
continue;
if (afd == -1) {
"problem(%d) opening xattr file [%s][%s]", errno,
goto tear_down;
}
/* We only can read upto one section extended attribute */
while (section_size > 0) {
char *buf;
long actual_size;
int read_size;
int sysattr_read = 0;
char *rec;
int size;
/*
* check for Abort commands
*/
goto tear_down;
}
if (!buf)
goto tear_down;
if ((actual_size < section_size) &&
if (!buf)
goto tear_down;
size = actual_size;
sysattr_read = 1;
}
/*
* check for Abort commands
*/
goto tear_down;
}
break;
if (sysattr_read) {
size, local_commands) == 0) {
goto tear_down;
}
}
if (actual_size == -1) {
"problem(%d) reading file [%s][%s]",
goto tear_down;
}
seek_spot += actual_size;
}
afd = -1;
}
if (afd > 0)
/* closedir closes fd too */
return (rv);
}
/*
* tlm_output_file
*
* Put this file into the output buffers.
*/
{
char *fullname; /* directory + name */
char *snapname; /* snapshot name */
char *linkname; /* where this file points */
int section = 0; /* section of a huge file */
int fd;
/* for Multi Volume record */
char *fnamep;
/* Indicate whether a file with the same inode has been backed up. */
int hardlink_done = 0;
/*
* If a file with the same inode has been backed up, hardlink_pos holds
* the tape offset of the data record.
*/
u_longlong_t hardlink_pos = 0;
return (-TLM_NO_SCRATCH_SPACE);
}
goto err_out;
}
goto err_out;
}
if (file_size < 0) {
goto err_out;
}
}
/*
* Since soft links can not be read(2), we should only
* backup the file header.
*/
(void) output_file_header(fullname,
return (0);
}
/*
* For hardlink, only read the data if no other link
* belonging to the same inode has been backed up.
*/
}
if (!hardlink_done) {
/*
* Open the file for reading.
*/
if (fd == -1) {
"BACKUP> Can't open file [%s][%s] err(%d)",
goto err_out;
}
} else {
fd = -1;
}
linkname[0] = 0;
/*
* section = 0: file is small enough for TAR
* section > 0: file goes out in TLM_MAX_TAR_IMAGE sized chunks
* and the file name gets munged
*/
if (file_size > TLM_MAX_TAR_IMAGE) {
local_commands) < 0) {
goto err_out;
}
section = 1;
} else {
section = 0;
}
/*
* For hardlink, if other link belonging to the same inode
* has been backed up, only backup an empty record.
*/
if (hardlink_done)
file_size = 0;
/*
* work
*/
if (file_size == 0) {
(void) output_file_header(fullname,
/*
* this can fall right through since zero size files
* will be skipped by the WHILE loop anyway
*/
}
while (file_size > 0) {
(void) output_file_header(fullname,
while (section_size > 0) {
char *buf;
long actual_size;
int read_size;
/*
* check for Abort commands
*/
goto tear_down;
}
if (!buf)
goto tear_down;
/*
* check for Abort commands
*/
goto tear_down;
}
if (actual_size == 0)
break;
if (actual_size == -1) {
"problem(%d) reading file [%s][%s]",
goto tear_down;
}
seek_spot += actual_size;
file_size -= actual_size;
}
section++;
}
/*
* If data belonging to this hardlink has been backed up, add the link
* to hardlink queue.
*/
"backed up hardlink file %s, inode = %llu, pos = %llu ",
}
/*
* For hardlink, if other link belonging to the same inode has been
* backed up, no add_node entry should be sent for this link.
*/
if (hardlink_done) {
"backed up hardlink link %s, inode = %llu, pos = %llu ",
} else {
}
pos);
return (real_size);
}
/*
* tar_putfile
*
* Main file backup function for tar
*/
int
struct hardlink_q *hardlink_q)
{
int rv;
if (rv < 0)
return (rv);
}
/*
* get_write_buffer
*
* a wrapper to tlm_get_write_buffer so that
* we can cleanly detect ABORT commands
* without involving the TLM library with
* our problems.
*/
static char *
{
if (rec != 0) {
return (rec);
}
}
return (NULL);
}
#define NDMP_MORE_RECORDS 2
/*
* write_tar_eof
*
* This function is initially written for NDMP support. It appends
* two tar headers to the tar file, and also N more empty buffers
* to make sure that the two tar headers will be read as a part of
* a mover record and don't get locked because of EOM on the mover
* side.
*/
void
{
int i;
long actual_size;
/*
* output 2 zero filled records,
* TAR wants this.
*/
(void) get_write_buffer(sizeof (tlm_tar_hdr_t),
(void) get_write_buffer(sizeof (tlm_tar_hdr_t),
/*
* NDMP: Clear the rest of the buffer and write two more buffers
* to the tape.
*/
for (i = 0; i < NDMP_MORE_RECORDS &&
/*
* We don't need the return value of get_write_buffer(),
* since it's already zeroed out if the buffer is returned.
*/
}
}
/*
* Callback to backup each ZFS property
*/
static int
{
char vbuf[ZFS_MAXPROPLEN];
char sbuf[ZFS_MAXPROPLEN];
char *sourcestr;
return (ZPROP_INVAL);
return (ZPROP_INVAL);
return (ZPROP_CONT);
}
switch (stype) {
case ZPROP_SRC_NONE:
sourcestr = "none";
break;
case ZPROP_SRC_RECEIVED:
sourcestr = "received";
break;
case ZPROP_SRC_LOCAL:
break;
case ZPROP_SRC_TEMPORARY:
sourcestr = "temporary";
break;
case ZPROP_SRC_DEFAULT:
sourcestr = "default";
break;
default:
break;
}
return (ZPROP_CONT);
}
/*
*/
static int
{
char *typestr;
return (ZPROP_INVAL);
return (ZPROP_INVAL);
typestr = "userquota";
else
typestr = "groupquota";
else
return (0);
}
/*
* Callback to count each ZFS property
*/
/*ARGSUSED*/
static int
{
(*(int *)pp)++;
return (ZPROP_CONT);
}
/*
*/
/*ARGSUSED*/
static int
{
(*(int *)pp)++;
return (0);
}
/*
*/
int
{
int count = 0;
return (0);
&count);
&count);
count++;
return (count);
}
/*
* Notifies ndmpd that the metadata associated with the given ZFS dataset
* should be backed up.
*/
int
{
long actual_size;
const char *pname;
int pcount;
return (-1);
return (-1);
(void) mutex_lock(&zlib_mtx);
(void) mutex_unlock(&zlib_mtx);
return (-1);
}
size = sizeof (ndmp_metadata_header_ext_t) +
pcount * sizeof (ndmp_metadata_property_ext_t);
(void) mutex_unlock(&zlib_mtx);
return (-1);
}
/* Get all the ZFS properties */
/* Get user properties */
(void) mutex_unlock(&zlib_mtx);
return (-1);
}
}
zfs_put_quota_cb, &mhd);
zfs_put_quota_cb, &mhd);
(void) mutex_unlock(&zlib_mtx);
actual_size : size);
sz = actual_size;
}
}
}
return (0);
}