/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
* 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 <errno.h>
#include <stdlib.h>
#include <string.h>
#include "ndmpd_common.h"
#include "ndmpd.h"
/*
* ************************************************************************
* NDMP V2 HANDLERS
* ************************************************************************
*/
/*
* ndmpd_data_get_state_v2
*
* Request handler. Returns current data state.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
"sending data_get_state reply");
}
/*
* ndmpd_data_start_backup_v2
*
* Request handler. Starts a backup.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
/*
* start_backup sends the reply if the backup is successfully started.
* Otherwise, send the reply containing the error here.
*/
if (err != NDMP_NO_ERR) {
"sending data_start_backup reply");
}
}
/*
* ndmpd_data_start_recover_v2
*
* Request handler. Starts a restore.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
/*
* start_recover sends the reply if the recover is successfully started.
* Otherwise, send the reply containing the error here.
*/
if (err != NDMP_NO_ERR) {
"sending ndmp_data_start_recover_request_v2 reply");
}
}
/*
* ndmpd_data_get_env_v2
*
* Request handler. Returns the environment variable array sent
* with the backup request. This request may only be sent with
* a backup operation is in progress.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
} else {
}
}
/*
* ndmpd_data_stop_v2
*
* Request handler. Stops the current data operation.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
"sending data_stop reply");
return;
}
/* prepare for another data operation */
(void) ndmpd_data_init(session);
}
/*
* ndmpd_data_abort_v2
*
* state is not changed to the halted state until after the operation
* has actually been aborted and the notify_halt request has been sent.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
"sending data_abort reply");
return;
}
/*
* Don't go to HALTED state yet. Need to wait for data operation to
* abort. When this happens, ndmpd_done will get called and will
* perform the halt processing.
*/
}
/*
* ************************************************************************
* NDMP V3 HANDLERS
* ************************************************************************
*/
/*
* ndmpd_data_get_state_v3
*
* Request handler. Returns current data state.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
else
reply.est_time_remain = 0;
"sending ndmp_data_get_state_v3 reply");
}
/*
* ndmpd_data_start_backup_v3
*
* Request handler. Starts a backup.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
"Can't start new backup in current state.");
"Connection to the mover is not established.");
goto _error;
}
goto _error;
}
}
} else {
"Supported backup types are tar, dump, and zfs.");
goto _error;
}
} else {
}
/*
* *_start_backup* sends the reply if the backup is
* successfully started. Otherwise, send the reply
* containing the error here.
*/
"sending data_start_backup_v3 reply");
}
}
/*
* ndmpd_data_start_recover_v3
*
* Request handler. Starts a restore.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
goto _error;
}
} else {
"Supported backup types are tar, dump, and zfs.");
goto _error;
}
} else {
}
/*
* *_start_recover* sends the reply if the recover is
* successfully started. Otherwise, send the reply
* containing the error here.
*/
"sending data_start_recover_v3 reply");
}
}
/*
* ndmpd_data_abort_v3
*
* state is not changed to the halted state until after the operation
* has actually been aborted and the notify_halt request has been sent.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
case NDMP_DATA_STATE_IDLE:
break;
case NDMP_DATA_STATE_ACTIVE:
/*
* Don't go to HALTED state yet. Need to wait for data
* operation to abort. When this happens, ndmpd_done_v3
* will get called and will perform the halt processing.
*/
break;
case NDMP_DATA_STATE_HALTED:
case NDMP_DATA_STATE_LISTEN:
break;
default:
}
"sending data_abort_v3 reply");
}
/*
* ndmpd_data_stop_v3
*
* Request handler. Stops the current data operation.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
"sending data_stop_v3 reply");
return;
}
/* prepare for another data operation */
(void) ndmpd_data_init(session);
"sending data_stop_v3 reply");
}
/*
* ndmpd_data_listen_v3
*
* Request handler. Configures the server to listen for a connection
* from a remote mover.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
"Invalid internal data state to process listen request.");
"Invalid mover state to process listen request.");
} else {
}
"ndmp_data_listen_request_v3 reply");
return;
}
case NDMP_ADDR_LOCAL:
break;
case NDMP_ADDR_TCP:
break;
}
break;
default:
break;
}
"ndmp_data_listen_request_v3 reply");
}
/*
* ndmpd_data_connect_v3
*
* Request handler. Connects the data server to either a local
* or remote mover.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
} else {
}
"sending ndmp_data_connect_v3 reply");
return;
}
case NDMP_ADDR_LOCAL:
/*
* Verify that the mover is listening for a
* local connection
*/
"Mover is not in local listen state.");
} else {
}
break;
case NDMP_ADDR_TCP:
break;
default:
}
"sending ndmp_data_connect_v3 reply");
}
/*
* ************************************************************************
* NDMP V4 HANDLERS
* ************************************************************************
*/
/*
* ndmpd_data_get_env_v4
*
* Request handler. Returns the environment variable array sent
* with the backup request. This request may only be sent with
* a backup operation is in progress.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
} else {
}
}
/*
* ndmpd_data_get_state_v4
*
* Request handler. Returns current data state.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
else
reply.est_time_remain = 0;
"sending ndmp_data_get_state_v4 reply");
}
/*
* ndmpd_data_connect_v4
*
* Request handler. Connects the data server to either a local
* or remote mover.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
} else {
}
"sending ndmp_data_connect_v4 reply");
return;
}
case NDMP_ADDR_LOCAL:
/*
* Verify that the mover is listening for a
* local connection
*/
"Mover is not in local listen state.");
} else {
}
break;
case NDMP_ADDR_TCP:
break;
default:
}
"sending ndmp_data_connect_v4 reply");
}
/*
* ndmpd_data_listen_v4
*
* Request handler. Configures the server to listen for a connection
* from a remote mover.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
void
{
"Invalid internal data state to process listen request.");
"Invalid mover state to process listen request.");
} else {
}
"ndmp_data_listen_request_v4 reply");
return;
}
case NDMP_ADDR_LOCAL:
break;
case NDMP_ADDR_TCP:
break;
}
ndmp_malloc(sizeof (ndmp_tcp_addr_v4));
ndmp_malloc(sizeof (ndmp_tcp_addr_v4));
/* Copy that to data_addr for compatibility */
break;
default:
break;
}
"ndmp_data_listen_request_v4 reply");
}
/*
* ndmpd_data_start_recover_filehist_v4
*
* Request handler. Recovers the file history (not supported yet)
* This command has an optional support in V4.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
*
* Returns:
* void
*/
/*ARGSUSED*/
void
{
"sending ndmp_data_start_recover_filehist_reply_v4 reply");
}
/*
* ************************************************************************
* LOCALS
* ************************************************************************
*/
/*
* ndmpd_data_error_send
*
* This function sends the notify message to the client.
*
* Parameters:
* session (input) - session pointer.
* reason (input) - halt reason.
*
* Returns:
* Error code
*/
/*ARGSUSED*/
static int
{
}
/*
* ndmpd_data_error_send_v4
*
* This function sends the notify message to the client.
*
* Parameters:
* session (input) - session pointer.
* reason (input) - halt reason.
*
* Returns:
* Error code
*/
/*ARGSUSED*/
static int
{
}
/*
* ndmpd_data_error
*
* This function is called when a data error has been detected.
* A notify message is sent to the client and the data server is
* placed into the halted state.
*
* Parameters:
* session (input) - session pointer.
* reason (input) - halt reason.
*
* Returns:
* void
*/
void
{
return;
/*
*/
/*
* If mover local and successful backup, write any
* remaining buffered data to tape.
*/
(void) ndmpd_local_write_v3(session, 0, 0);
}
"Error sending notify_data_halted request");
} else {
"Error sending notify_data_halted request");
}
(void) ndmpd_remove_file_handler(session,
/*
* ndmpcopy: we use the same socket for the mover,
* so expect to close when mover is done!
*/
}
(void) ndmpd_remove_file_handler(session,
}
} else {
}
}
/*
* data_accept_connection_v3
*
* Accept a data connection from a remote mover.
* Called by ndmpd_select when a connection is pending on
* the data listen socket.
*
* Parameters:
* cookie (input) - session pointer.
* fd (input) - file descriptor.
* mode (input) - select mode.
*
* Returns:
* void
*/
/*ARGSUSED*/
static void
{
int from_len;
&from_len);
return;
}
/*
* Save the peer address.
*/
/* Set the parameter of the new socket */
}
/*
* create_listen_socket_v3
*
* incoming connections.
*/
static int
{
return (-1);
/*
* Add a file handler for the listen socket.
* ndmpd_select will call data_accept_connection when a
* connection is ready to be accepted.
*/
data_accept_connection_v3) < 0) {
return (-1);
}
return (0);
}
/*
* data_connect_sock_v3
*
*
* Parameters:
* session (input) - session pointer.
* addr (input) - IP address
* port (input) - port number
*
* Returns:
* NDMP_NO_ERR - backup successfully started.
* otherwise - error code of backup start error.
*/
static ndmp_error
{
int sock;
if (sock < 0)
return (NDMP_CONNECT_ERR);
return (NDMP_NO_ERR);
}
/*
* ndmpd_tar_start_backup_v3
*
* Start the backup work
*
* Parameters:
* session (input) - session pointer.
* bu_type (input) - backup type.
* env_val (input) - environment variable array.
* env_len (input) - length of env_val.
*
* Returns:
* NDMP_NO_ERR - backup successfully started.
* otherwise - error code of backup start error.
*/
static ndmp_error
{
int err;
if (err != NDMP_NO_ERR)
return (err);
if (!params)
return (NDMP_NO_MEM_ERR);
params->mp_get_name_func = 0;
else
params->mp_read_func = 0;
params->mp_file_recovered_func = 0;
} else {
}
}
&reply) < 0) {
return (NDMP_NO_ERR);
}
/*
* perform the backup
*
* Cannot wait for the thread to exit as we are replying to the
* client request here.
*/
params);
if (err != 0) {
return (NDMP_ILLEGAL_ARGS_ERR);
}
return (NDMP_NO_ERR);
}
/*
* ndmpd_tar_start_recover_v3
*
* Start the restore work
*
* Parameters:
* session (input) - session pointer.
* bu_type (input) - backup type.
* env_val (input) - environment variable array.
* env_len (input) - length of env_val.
* nlist_val (input) - list of files.
* nlist_len (input) - length of nlist_val.
*
* Returns:
* NDMP_NO_ERR - recover successfully started.
* otherwise - error code of recover start error.
*/
static ndmp_error
{
int err;
if (!params) {
return (NDMP_NO_MEM_ERR);
}
return (NDMP_NO_MEM_ERR);
}
return (NDMP_NO_MEM_ERR);
}
/*
* Setup restore parameters.
*/
} else {
}
params->mp_write_func = 0;
if (err != NDMP_NO_ERR) {
return (err);
}
&reply) < 0) {
"Error sending ndmp_data_start_recover_reply");
return (NDMP_NO_ERR);
}
/*
* perform the restore
*
* Cannot wait for the thread to exit as we are replying to the
* client request here.
*/
params);
if (err != 0) {
return (NDMP_ILLEGAL_ARGS_ERR);
}
return (NDMP_NO_ERR);
}
static ndmp_error
enum ndmp_data_operation op)
{
void *reply;
int err;
if (ndmpd_zfs_init(session) != 0)
return (NDMP_UNDEFINED_ERR);
if (err != NDMP_NO_ERR) {
return (err);
}
switch (op) {
case NDMP_DATA_OP_BACKUP:
return (NDMP_ILLEGAL_ARGS_ERR);
}
if (ndmpd_zfs_pre_backup(ndmpd_zfs_args)) {
return (NDMP_ILLEGAL_ARGS_ERR);
}
break;
case NDMP_DATA_OP_RECOVER:
if (err != NDMP_NO_ERR) {
return (NDMP_NO_MEM_ERR);
}
return (NDMP_ILLEGAL_ARGS_ERR);
}
if (ndmpd_zfs_pre_restore(ndmpd_zfs_args)) {
(void) ndmpd_zfs_post_restore(ndmpd_zfs_args);
return (NDMP_ILLEGAL_ARGS_ERR);
}
break;
}
if (op == NDMP_DATA_OP_BACKUP) {
reply = &backup_reply;
} else {
reply = &recover_reply;
}
reply) < 0) {
if (op == NDMP_DATA_OP_RECOVER)
return (NDMP_NO_ERR);
}
if (err) {
return (NDMP_NO_ERR);
}
(void) pthread_detach(tid);
if (op == NDMP_DATA_OP_BACKUP)
else
"'zfs' %s starting\n", str);
return (NDMP_NO_ERR);
}
/*
* discard_data_v3
*
* Read and discard data from the data connection.
* Called when a module has called ndmpd_seek() prior to
* reading all of the data from the previous seek.
*
* Parameters:
* session (input) - session pointer.
*
* Returns:
* number of bytes read and discarded.
* -1 - error.
*/
static int
{
int n, toread;
/* Read and discard the data. */
if (n < 0) {
n = -1;
}
return (n);
}
/*
* ndmpd_remote_read_v3
*
* Reads data from the remote mover.
*
* Parameters:
* session (input) - session pointer.
* data (input) - data to be written.
* length (input) - data length.
*
* Returns:
* 0 - data successfully read.
* -1 - error.
*/
int
{
ssize_t n;
count = 0;
/*
* If the end of the seek window has been reached then
* send an ndmp_read request to the client.
* The NDMP client will then send a mover_data_read request to
* the remote mover and the mover will send more data.
* This condition can occur if the module attempts to read past
* a seek window set via a prior call to ndmpd_seek() or
* the module has not issued a seek. If no seek was issued then
* pretend that a seek was issued to read the entire tape.
*/
/* ndmpd_seek() never called? */
} else {
/*
* While restoring a file, restoreFile()
* records the number of bytes still need to
* be restored. We use this as a guidance
* when asking for data from the tape.
*/
/*
* Fall back to the old way if fsize if too
* small.
*/
}
&request, 0) < 0) {
"Sending notify_data_read request");
return (-1);
}
}
/*
* If the module called ndmpd_seek() prior to reading all of the
* data that the remote mover was requested to send, then the
* excess data from the seek has to be discarded.
*/
n = discard_data_v3(session,
if (n < 0)
return (-1);
continue;
}
/*
* Don't attempt to read more data than the remote is sending.
*/
len)) < 0) {
return (-1);
}
/* read returns 0 if the connection was closed */
if (n == 0) {
errno);
return (-1);
}
count += n;
}
return (0);
}
/*
* nlp_release_job_stat
*
* Unreference the job statistics
*
* Parameters:
* session (input) - session pointer.
*
* Returns:
* void
*/
static void
{
return;
}
} else
}
/* *** ndmpd global internal functions *********************************** */
/*
* ndmpd_data_init
*
* Initializes data specific session variables.
*
* Parameters:
* session (input) - session pointer.
*
* Returns:
* void
*/
int
{
/*
* NDMP V3
*/
return (0);
}
/*
* ndmpd_data_cleanup
*
* Releases resources allocated during a data operation.
*
* Parameters:
* session (input) - session pointer.
*
* Returns:
* void
*/
void
{
(void) ndmpd_remove_file_handler(session,
}
/*
* ndmpcopy: we use the same socket for the mover,
* so expect to close when mover is done!
*/
}
}
/*
* ndmp_data_get_mover_mode
*
* Return the mover mode
*
* Parameters:
* session (input) - session pointer.
*
* Returns:
* remote - remote backup
* local - local backup
*/
char *
{
char *rv;
switch (session->ns_protocol_version) {
case NDMPV2:
? "remote" : "local");
break;
case NDMPV3:
? "remote" : "local");
break;
case NDMPV4:
break;
default:
rv = "Unknown";
}
return (rv);
}
/* *** static functions ******************************************** */
/*
* ndmpd_tar_start_backup_v2
*
* Request handling code common to version 1 and
* version 2 data_start_backup request handlers.
*
* Parameters:
* session (input) - session pointer.
* bu_type (input) - backup type.
* env_val (input) - environment variable array.
* env_len (input) - length of env_val.
*
* Returns:
* NDMP_NO_ERR - backup successfully started.
* otherwise - error code of backup start error.
*/
static ndmp_error
{
int err;
return (NDMP_ILLEGAL_STATE_ERR);
}
return (NDMP_ILLEGAL_ARGS_ERR);
}
return (err);
return (NDMP_NO_MEM_ERR);
params->mp_read_func = 0;
params->mp_file_recovered_func = 0;
/* backup type == NDMP_TAR_TYPE */
} else {
}
params)) != NDMP_NO_ERR) {
return (err);
}
if (err != NDMP_NO_ERR) {
"mover connect err: %d", err);
return (err);
}
&reply) < 0) {
/*
* ndmpcopy: we use the same socket for the mover,
* so expect to close when mover is done!
*/
} else
return (NDMP_NO_ERR);
}
/*
* perform the backup
*
* Cannot wait for the thread to exit as we are replying to the
* client request here.
*/
params);
return (NDMP_NO_ERR);
}
/*
* ndmpd_tar_start_recover_v2
*
*
* Parameters:
* session (input) - session pointer.
* bu_type (input) - backup type.
* env_val (input) - environment variable array.
* env_len (input) - length of env_val.
* nlist_val (input) - list of files.
* nlist_len (input) - length of nlist_val.
*
* Returns:
* NDMP_NO_ERR - recover successfully started.
* otherwise - error code of backup start error.
*/
static ndmp_error
{
int err;
return (NDMP_ILLEGAL_STATE_ERR);
}
return (NDMP_ILLEGAL_ARGS_ERR);
}
return (NDMP_NO_MEM_ERR);
return (NDMP_NO_MEM_ERR);
return (NDMP_NO_MEM_ERR);
/*
* Setup restore parameters.
*/
params->mp_write_func = 0;
params)) != NDMP_NO_ERR) {
return (err);
}
if (err != NDMP_NO_ERR) {
return (err);
}
&reply) < 0) {
/*
* ndmpcopy: we use the same socket for the mover,
* so expect to close when mover is done!
*/
} else {
}
return (NDMP_NO_ERR);
}
/*
* perform the restore
*
* Cannot wait for the thread to exit as we are replying to the
* client request here.
*/
params);
return (NDMP_NO_ERR);
}
/*
* ndmpd_data_get_info
*
* Return the total number of bytes processed
*
* Parameters:
* session (input) - session pointer.
*
* Returns:
* the number of bytes processed
*/
static u_longlong_t
{
return ((u_longlong_t)0);
return (nlp->nlp_bytes_total);
}