/*
*/
/*
* 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 */
/* Copyright 2014 Nexenta Systems, Inc. All rights reserved. */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <cstack.h>
#include <dirent.h>
#include <traverse.h>
#include "bitmap.h"
#include "ndmpd.h"
#include "tlm_buffers.h"
typedef struct ndmp_run_args {
char *nr_chkp_nm;
char *nr_unchkp_nm;
char **nr_excls;
/*
* backup_create_structs
*
* Allocate the structures before performing backup
*
* Parameters:
* sesison (input) - session handle
* jname (input) - backup job name
*
* Returns:
* 0: on success
* -1: otherwise
*/
static int
{
int n;
long xfer_size;
return (-1);
}
return (-1);
}
/*
* Read multiple of mover_record_size near to 512K. This
* will prevent the data being copied in the mover buffer
* when we write the data.
*/
n = 1;
xfer_size *= n;
}
return (-1);
}
return (-1);
}
return (0);
}
/*
* restore_create_structs
*
* Allocate structures for performing a restore
*
* Parameters:
* sesison (input) - session handle
* jname (input) - backup job name
*
* Returns:
* 0: on success
* -1: otherwise
*/
static int
{
int i;
long xfer_size;
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
for (i = 0; i < (int)nlp->nlp_nfiles; i++)
return (0);
}
/*
* send_unrecovered_list
*
* Creates a list of restored files
*
* Parameters:
* params (input) - NDMP parameters
*
* Returns:
* 0: on success
* -1: otherwise
*/
static int
{
int i, rv;
return (-1);
}
return (-1);
}
rv = 0;
for (i = 0; i < (int)nlp->nlp_nfiles; i++) {
if (!nlp->nlp_restored[i]) {
rv = -1;
break;
}
rv = -1;
break;
}
if (rv < 0)
break;
}
}
return (rv);
}
/*
* backup_release_structs
*
*
* Parameters:
* session (input) - session handle
*
* Returns:
* void
*/
/*ARGSUSED*/
static void
{
return;
}
return;
}
} else {
}
else
} else {
}
}
} else {
}
}
/*
* ndmp_write_utf8magic
*
* Write a magic pattern to the tar header. This is used
* as a crest to indicate that tape belongs to us.
*/
int
{
char *cp;
long actual_size;
return (-1);
}
if (actual_size < RECORDSIZE) {
return (-1);
}
return (0);
}
/*
* timecmp
*
* This callback function is used during backup. It checks
* if the object specified by the 'attr' should be backed
* up or not.
*
* Directories are backed up anyways for dump format.
* If this function is called, then the directory is
* marked in the bitmap vector, it shows that either the
* directory itself is modified or there is something below
* it that will be backed up.
*
* Directories for tar format are backed up if and only if
* they are modified.
*
* By setting ndmp_force_bk_dirs global variable to a non-zero
* value, directories are backed up anyways.
*
* Backing up the directories unconditionally, helps
* restoring the metadata of directories as well, when one
* of the objects below them are being restored.
*
* For non-directory objects, if the modification or change
* time of the object is after the date specified by the
* bk_selector_t, the the object must be backed up.
*
*/
static boolean_t
{
return (TRUE);
}
/*
* If the object is a directory and it leads to a modified
* object (that should be backed up) and for that type of
* backup the path nodes should be backed up, then return
* TRUE.
*
* This is required by some DMAs like Backup Express, which
* needs to receive ADD_NODE (for dump) or ADD_PATH (for tar)
* for the intermediate directories of a modified object.
* Other DMAs, like net_backup and net_worker, do not have such
* requirement. This requirement makes sense for dump format
* but for 'tar' format, it does not. In provision to the
* NDMP-v4 spec, for 'tar' format the intermediate directories
* need not to be reported.
*/
return (TRUE);
}
return (TRUE);
}
if (NLP_IGNCTIME(nlp)) {
return (FALSE);
}
return (TRUE);
}
return (FALSE);
}
/*
* get_acl_info
*
* load up all the access and attribute info
*/
static int
{
int erc;
char *acltp;
if (erc != 0) {
return (erc);
}
if (erc != 0) {
"Could not read ACL for file [%s]", name);
return (erc);
}
}
return (erc);
}
/*
* get_dir_acl_info
*
* load up all ACL and attr info about a directory
*/
static int
{
int erc;
char *checkpointed_dir;
char *spot;
char *fil;
char *acltp;
if (checkpointed_dir == NULL)
return (-1);
if (tlm_acls->acl_checkpointed)
js->js_job_name);
else
if (erc != 0) {
return (-1);
}
} else {
*spot = 0;
*spot = '/';
}
if (erc != 0) {
root_dir);
return (-1);
}
}
if (erc != 0) {
"Could not read metadata for directory [%s]", dir);
return (-1);
}
}
return (0);
}
/*
* backup_dir
*
* Create a TAR entry record for a directory
*/
static int
{
int erc;
if (erc != 0) {
"Could not read directory info for %s", dir);
} else {
/*
* See if the directory must be backed up.
*/
return (erc);
}
}
return (erc);
}
/*
* backup_file
*
* Create a TAR record entry for a file
*/
static longlong_t
{
int erc;
/*
* get_acl_info extracts file handle, attributes and ACLs of the file.
* This is not efficient when the attributes and file handle of
* the file is already known.
*/
if (erc != TLM_NO_ERRORS) {
return (-ENOENT);
}
/* Should the file be backed up? */
if (!bksp) {
return (0);
}
/* Only the regular files and symbolic links can be backed up. */
return (-EINVAL);
}
return (rv);
}
/*
* backup_work
*
* Start the NDMP backup (V2 only).
*/
int
{
char *dname;
int erc;
int retval;
unsigned long fileid;
int dname_size;
long dpos;
/* Get every name in this directory */
return (-ENOMEM);
retval = 0;
/*
* should we skip the whole thing?
*/
return (0);
}
/*
* Search for the top-level file-directory
*/
if (NLP_ISCHKPNTED(nlp)) {
} else {
}
if (erc != 0) {
return (-EINVAL);
}
return (-ENOMEM);
}
/*
* Push the first name onto the stack so that we can pop it back
* off as part of the normal cycle
*/
return (-ENOMEM);
}
/*
* Did NDMP create a checkpoint?
*/
} else {
/* Use the checkpoint created by NDMP */
}
/*
* This is level-backup. It never resets the archive bit.
*/
if (NLP_ISCHKPNTED(nlp))
else
np->nr_unchkp_nm);
if (erc == 0) {
} else {
"Filesystem readdir in [%s]",
break;
}
/* an empty name size marks the end of the list */
if (dname_size == 0)
break;
/*
* If name refers to a directory, push its file
* handle onto the stack (skip "." and "..").
*/
if (rootfs_dot_or_dotdot(dname)) {
fileid = 0;
continue;
}
/*
* Skip the:
* non-dir entries which should not be backed up
* Or
* dir-type entries which have have nothing under
* their hierarchy to be backed up.
*/
fileid = 0;
continue;
}
fileid = 0;
continue;
}
/*
* only directories get pushed onto this stack,
* so we do not have to test for regular files.
*/
if (t_dir_info == NULL) {
"While backing up [%s][%s]",
0) != 0) {
"No enough memory stack_push");
break;
}
if (fsize >= 0) {
} else
fileid = 0;
}
}
fileid = 0;
if (retval != 0)
break;
}
}
return (retval);
}
/*
* free_paths
*
* Free the path names
*/
static void
{
}
/*
* malloc_paths
*
* Allocate the path names (direct and checkpointed paths)
*/
static boolean_t
{
free_paths(np);
free_paths(np);
}
return (rv);
}
/*
* ndmp_backup_reader
*
* Backup reader thread which uses backup_work to read and TAR
*/
static int
char *job_name)
{
int retval;
if (!malloc_paths(&np))
return (-1);
local_commands->tc_ref++;
free_paths(&np);
return (retval);
}
/*
* ndmp_tar_writer
*
* The backup writer thread that writes the TAR records to the
* tape media (V2 only)
*/
int
{
int err;
err = 0;
err = -1;
} else if (mod_params == NULL) {
err = -1;
err = -1;
}
if (err != 0)
return (err);
cmds->tcs_writer_count++;
nw = 0;
buf->tb_buffer_size) != 0) {
"Writing buffer %d, pos: %lld",
err = -1;
break;
}
(void) tlm_buffer_advance_out_idx(bufs);
nw++;
} else {
/* No more data is comming; time to exit. */
"tc_writer!=TLM_BACKUP_RUN; time to exit");
break;
} else {
}
}
}
} else {
}
} else {
}
cmds->tcs_writer_count--;
return (err);
}
/*
* read_one_buf
*
* Read one buffer from the tape
*/
static int
{
int rv;
bufs->tbs_data_transfer_size)) == 0) {
buf->tb_buffer_spot = 0;
(void) tlm_buffer_advance_in_idx(bufs);
}
return (rv);
}
/*
* ndmp_tar_reader
*
* NDMP Tar reader thread. This threads keep reading the tar
* file from the tape and wakes up the consumer thread to extract
* it on the disk
*/
int
{
int bidx;
int err;
if (!argp)
return (-1);
err = 0;
err = -1;
err = -1;
}
if (err != 0) {
return (err);
}
cmds->tcs_reader_count++;
/*
* Synchronize with our parent thread.
*/
/*
* The buffer is still full, wait for the consumer
* thread to use it.
*/
} else {
if (err < 0) {
"Reading buffer %d, pos: %lld",
/* Force the writer to stop. */
break;
} else if (err == 1) {
"operation aborted or session terminated");
err = 0;
break;
}
}
}
/*
* If the consumer is waiting for us, wake it up so that it detects
* we're quiting.
*/
(void) usleep(1000);
/*
* Clean up.
*/
cmds->tcs_reader_count--;
return (err);
}
/*
* ndmpd_tar_backup
*
* Check must have been done that backup work directory exists, before
* calling this function.
*/
static int
{
int err;
"mod_params->mp_operation != NDMP_DATA_OP_BACKUP");
err = -1;
} else {
err = -1;
else if (ndmp_get_bk_dir_ino(nlp))
err = -1;
else
err = 0;
}
if (err != 0)
return (err);
(void) ndmp_new_job_name(jname);
return (-1);
return (-1);
}
if (err != 0) {
return (-1);
}
/* Act as the writer thread. */
"Runtime [%s] %llu bytes (%llu): %d seconds",
"Runtime [%s] %llu bytes (%llu): %d seconds",
err = -1;
} else {
err = 0;
}
return (err);
}
/*
* ndmpd_tar_restore
*
* Restore function that launches TAR reader thread to read from the
*/
static int
{
char *rspath;
int err;
int i;
"mod_params->mp_operation != NDMP_DATA_OP_RECOVER");
return (-1);
}
else
rspath = "";
(void) ndmp_new_job_name(jname);
return (-1);
(void *)&arg);
if (err == 0) {
} else {
return (-1);
}
} else {
}
/*
* Set up restore parameters
*/
for (i = 0; i < nlp->nlp_nfiles; i++) {
}
} else {
"Thread create tm_getfile: ops NULL");
return (-1);
}
if (err == 0) {
} else {
return (-1);
}
} else {
/* nothing restored. */
rspath);
err = -1;
}
return (err);
}
/*
* prefixdir
*
* Extract the path for a given full path entry
*/
static char *
{
return (NULL);
if (*suffix == '\0')
return (dir);
if (*dir == '\0')
return (NULL);
/*
* Move backward as far as the last part of the dir and
* the suffix match.
*/
else
break;
*++tend = '\0';
return (tmp);
}
/*
* get_nfiles
*
* Get the count of files to be restored
*/
static int
{
return (-1);
}
}
/*
* get_restore_dest
*
* Get the full pathname of where the entries should be restored to.
*/
static char *
{
char *cp;
/*
* Destination of restore:
* NetBackup of Veritas(C) sends the entries like this:
*
* ent[i].name: is the relative pathname of what is selected in
* the GUI.
* be restored to.
* ent[i].ssi: 0
* ent[i].fh_info: 0
*
*/
return (NULL);
}
return (cp);
}
/*
* correct_ents
*
* Correct the entries in the restore list by appending the appropriate
* path to them
*/
static int
{
return (-1);
}
rv = 0;
/* Append the backup path to all the "ent[].name"s. */
for (i = 0; i < n; i++) {
/* remove trailing slash */
rv = -1;
break;
}
/* Make a copy of the new string and save it in ent->name. */
rv = -1;
break;
}
}
return (rv);
}
/*
* check_restore_paths
*
* Go through the restore list and check the validity of the
* restore path.
*/
static int
{
int i, rv;
rv = 0;
if (!fs_volexist(rspath)) {
"Error: Invalid volume name for restore.");
rv = -1;
}
} else {
for (i = 0; i < n; i++) {
"Error: Invalid volume name for restore.",
rv = -1;
break;
}
}
}
return (rv);
}
/*
* check_backup_dir_validity
*
* Check if the backup directory is valid. Make sure it exists and
* is writable. Check for snapshot and readonly cases.
*/
static int
{
char *msg;
int rv;
rv = NDMP_NO_ERR;
bkpath);
}
return (rv);
}
/*
* ndmp_backup_extract_params
*
* Go through the backup parameters and check the validity
* for each one. Then set the NLP flags according to the parameters.
*/
int
{
char *cp;
int rv;
/* Extract directory to be backed up from env variables */
return (NDMP_ILLEGAL_ARGS_ERR);
}
return (NDMP_FILE_NOT_FOUND_ERR);
return (rv);
/* Should the st_ctime be ignored when backing up? */
if (ndmp_ignore_ctime) {
} else
/* Should the st_lmtime be ignored when backing up? */
if (ndmp_include_lmtime) {
} else
/* Is backup history requested? */
} else {
else
}
nlp->nlp_clevel = 0;
/* Is it an incremental backup? */
"env(LEVEL) not specified, default to 0");
return (NDMP_ILLEGAL_ARGS_ERR);
} else
/* Extract last backup time from the dumpdates file */
return (NDMP_NO_MEM_ERR);
}
"Date of this level %d on \"%s\": %s",
"Date of last level %d on \"%s\": %s",
/* Should the dumpdate file be updated? */
"env(UPDATE) not specified, default to TRUE");
} else {
else
}
return (NDMP_NO_ERR);
}
/*
* log_bk_params_v2
*
* Dump the value of the parameters in the log file for debugging.
*/
void
{
}
/*
* same_path
*
* Find out if the paths are the same regardless of the ending slash
*
* Examples :
* /a/b/c == /a/b/c
* /a/b/c/ == /a/b/c
* /a/b/c == /a/b/c/
*/
static boolean_t
same_path(char *s, char *t)
{
} else {
}
}
return (rv);
}
/*
* ndmp_restore_extract_params
*
* Go through the restore parameters and check them and extract them
* by setting NLP flags and other values.
*
* Parameters:
*
* Returns:
* 0: on success
* -1: otherwise
*/
int
{
return (-1);
}
/* Extract directory from where the backup was made. */
return (NDMP_ILLEGAL_ARGS_ERR);
/* The number of the selections. */
return (NDMP_ILLEGAL_ARGS_ERR);
return (NDMP_ILLEGAL_ARGS_ERR);
if (fs_is_rdonly(rspath)) {
"Error: Can't restore to a read-only volume: \"%s\"\n",
rspath);
return (NDMP_ILLEGAL_ARGS_ERR);
}
if (fs_is_chkpntvol(rspath)) {
"Error: Can't restore to a checkpoint: \"%s\"\n", rspath);
return (NDMP_ILLEGAL_ARGS_ERR);
}
rspath = "";
return (NDMP_NO_MEM_ERR);
return (NDMP_ILLEGAL_ARGS_ERR);
}
return (NDMP_ILLEGAL_ARGS_ERR);
}
return (NDMP_NO_ERR);
}
/*
* ndmpd_tar_backup_starter (V2 only)
*
* The main backup starter function. It creates a snapshot if necessary
* and calls ndmp_tar_backup to perform the actual backup. It does the cleanup
* and release the snapshot at the end.
*/
int
{
int err;
err = 0;
else {
"Error: creating checkpoint on %s\n",
/* -1 causes halt reason to become internal error. */
err = -1;
}
}
if (err == 0) {
if (err != 0) {
} else {
}
}
}
if (!NLP_ISCHKPNTED(nlp))
"Error: updating the dumpdates file on %s\n",
}
}
/* nlp_params is allocated in start_backup() */
return (err);
}
/*
* ndmpd_tar_backup_abort
*
* Abort the running backup by stopping the reader thread (V2 only)
*/
int
{
}
}
return (0);
}
/*
* ndmpd_tar_restore_starter
*
* Starts the restore by running ndmpd_tar_restore function (V2 only)
*/
int
{
int err;
/* nlp_params is allocated in start_recover() */
return (err);
}
/*
* ndmpd_tar_restore_abort
*
* Aborts the restore operation by stopping the writer thread (V2 only)
*/
int
{
}
}
return (0);
}