1 /*
   2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 
   5 /*
   6  * BSD 3 Clause License
   7  *
   8  * Copyright (c) 2007, The Storage Networking Industry Association.
   9  *
  10  * Redistribution and use in source and binary forms, with or without
  11  * modification, are permitted provided that the following conditions
  12  * are met:
  13  *      - Redistributions of source code must retain the above copyright
  14  *        notice, this list of conditions and the following disclaimer.
  15  *
  16  *      - Redistributions in binary form must reproduce the above copyright
  17  *        notice, this list of conditions and the following disclaimer in
  18  *        the documentation and/or other materials provided with the
  19  *        distribution.
  20  *
  21  *      - Neither the name of The Storage Networking Industry Association (SNIA)
  22  *        nor the names of its contributors may be used to endorse or promote
  23  *        products derived from this software without specific prior written
  24  *        permission.
  25  *
  26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36  * POSSIBILITY OF SUCH DAMAGE.
  37  */
  38 /* Copyright (c) 2007, The Storage Networking Industry Association. */
  39 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
  40 /* Copyright 2017 Nexenta Systems, Inc.  All rights reserved. */
  41 
  42 #include <sys/ioctl.h>
  43 #include <sys/types.h>
  44 #include <sys/socket.h>
  45 #include <sys/socketvar.h>
  46 #include <syslog.h>
  47 #include <netinet/in.h>
  48 #include <arpa/inet.h>
  49 #include <net/if.h>
  50 #include <errno.h>
  51 #include <fcntl.h>
  52 #include <netdb.h>
  53 #include <stdlib.h>
  54 #include <unistd.h>
  55 #include <string.h>
  56 #include "ndmpd_common.h"
  57 #include "ndmpd.h"
  58 #include <sys/mtio.h>
  59 
  60 /*
  61  * Maximum mover record size
  62  */
  63 #define MAX_MOVER_RECSIZE       (512*KILOBYTE)
  64 
  65 static int create_listen_socket_v2(ndmpd_session_t *session, ulong_t *addr,
  66     ushort_t *port);
  67 static int tape_read(ndmpd_session_t *session, char *data);
  68 static int change_tape(ndmpd_session_t *session);
  69 static int discard_data(ndmpd_session_t *session, ulong_t length);
  70 static int mover_tape_read_one_buf(ndmpd_session_t *session, tlm_buffer_t *buf);
  71 static int mover_socket_write_one_buf(ndmpd_session_t *session,
  72     tlm_buffer_t *buf);
  73 static int start_mover_for_restore(ndmpd_session_t *session);
  74 static int mover_socket_read_one_buf(ndmpd_session_t *session,
  75     tlm_buffer_t *buf, long read_size);
  76 static int mover_tape_write_one_buf(ndmpd_session_t *session,
  77     tlm_buffer_t *buf);
  78 static int start_mover_for_backup(ndmpd_session_t *session);
  79 static boolean_t is_writer_running_v3(ndmpd_session_t *session);
  80 static int mover_pause_v3(ndmpd_session_t *session,
  81     ndmp_mover_pause_reason reason);
  82 static int mover_tape_write_v3(ndmpd_session_t *session, char *data,
  83     ssize_t length);
  84 static int mover_tape_flush_v3(ndmpd_session_t *session);
  85 static int mover_tape_read_v3(ndmpd_session_t *session, char *data);
  86 static int create_listen_socket_v3(ndmpd_session_t *session, ulong_t *addr,
  87     ushort_t *port);
  88 static void mover_data_read_v3(void *cookie, int fd, ulong_t mode);
  89 static void accept_connection(void *cookie, int fd, ulong_t mode);
  90 static void mover_data_write_v3(void *cookie, int fd, ulong_t mode);
  91 static void accept_connection_v3(void *cookie, int fd, ulong_t mode);
  92 static ndmp_error mover_connect_sock(ndmpd_session_t *session,
  93     ndmp_mover_mode mode, ulong_t addr, ushort_t port);
  94 static boolean_t is_writer_running(ndmpd_session_t *session);
  95 static int set_socket_nonblock(int sock);
  96 
  97 
  98 int ndmp_max_mover_recsize = MAX_MOVER_RECSIZE; /* patchable */
  99 
 100 #define TAPE_READ_ERR           -1
 101 #define TAPE_NO_WRITER_ERR      -2
 102 
 103 /*
 104  * Set non-blocking mode for socket.
 105  */
 106 static int
 107 set_socket_nonblock(int sock)
 108 {
 109         int flags;
 110 
 111         flags = fcntl(sock, F_GETFL, 0);
 112         if (flags < 0)
 113                 return (0);
 114         return (fcntl(sock, F_SETFL, flags|O_NONBLOCK) == 0);
 115 }
 116 
 117 /*
 118  * ************************************************************************
 119  * NDMP V2 HANDLERS
 120  * ************************************************************************
 121  */
 122 
 123 /*
 124  * ndmpd_mover_get_state_v2
 125  *
 126  * This handler handles the mover_get_state request.
 127  * Status information for the mover state machine is returned.
 128  *
 129  * Parameters:
 130  *   connection (input) - connection handle.
 131  *   body       (input) - request message body.
 132  *
 133  * Returns:
 134  *   void
 135  */
 136 /*ARGSUSED*/
 137 void
 138 ndmpd_mover_get_state_v2(ndmp_connection_t *connection, void *body)
 139 {
 140         ndmp_mover_get_state_reply_v2 reply;
 141         ndmpd_session_t *session = ndmp_get_client_data(connection);
 142 
 143         reply.error = NDMP_NO_ERR;
 144         reply.state = session->ns_mover.md_state;
 145         reply.pause_reason = session->ns_mover.md_pause_reason;
 146         reply.halt_reason = session->ns_mover.md_halt_reason;
 147         reply.record_size = session->ns_mover.md_record_size;
 148         reply.record_num = session->ns_mover.md_record_num;
 149         reply.data_written =
 150             long_long_to_quad(session->ns_mover.md_data_written);
 151         reply.seek_position =
 152             long_long_to_quad(session->ns_mover.md_seek_position);
 153         reply.bytes_left_to_read =
 154             long_long_to_quad(session->ns_mover.md_bytes_left_to_read);
 155         reply.window_offset =
 156             long_long_to_quad(session->ns_mover.md_window_offset);
 157         reply.window_length =
 158             long_long_to_quad(session->ns_mover.md_window_length);
 159 
 160         ndmp_send_reply(connection, (void *) &reply,
 161             "sending tape_get_state reply");
 162 }
 163 
 164 
 165 /*
 166  * ndmpd_mover_listen_v2
 167  *
 168  * This handler handles mover_listen requests.
 169  *
 170  * Parameters:
 171  *   connection (input) - connection handle.
 172  *   body       (input) - request message body.
 173  *
 174  * Returns:
 175  *   void
 176  */
 177 void
 178 ndmpd_mover_listen_v2(ndmp_connection_t *connection, void *body)
 179 {
 180         ndmp_mover_listen_request_v2 *request;
 181         ndmp_mover_listen_reply_v2 reply;
 182         ndmpd_session_t *session = ndmp_get_client_data(connection);
 183         ulong_t addr;
 184         ushort_t port;
 185 
 186         request = (ndmp_mover_listen_request_v2 *)body;
 187 
 188         if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE ||
 189             session->ns_data.dd_state != NDMP_DATA_STATE_IDLE) {
 190                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 191                 ndmp_send_reply(connection, (void *) &reply,
 192                     "sending mover_listen reply");
 193                 return;
 194         }
 195         session->ns_mover.md_mode = request->mode;
 196 
 197         if (request->addr_type == NDMP_ADDR_LOCAL) {
 198                 reply.mover.addr_type = NDMP_ADDR_LOCAL;
 199         } else {
 200                 if (create_listen_socket_v2(session, &addr, &port) < 0) {
 201                         reply.error = NDMP_IO_ERR;
 202                         ndmp_send_reply(connection, (void *) &reply,
 203                             "sending mover_listen reply");
 204                         return;
 205                 }
 206                 reply.mover.addr_type = NDMP_ADDR_TCP;
 207                 reply.mover.ndmp_mover_addr_u.addr.ip_addr = htonl(addr);
 208                 reply.mover.ndmp_mover_addr_u.addr.port = htons(port);
 209         }
 210 
 211         session->ns_mover.md_state = NDMP_MOVER_STATE_LISTEN;
 212 
 213         /*
 214          * ndmp window should always set by client during restore
 215          */
 216 
 217         /* Set the default window. */
 218         session->ns_mover.md_window_offset = 0;
 219         session->ns_mover.md_window_length = MAX_WINDOW_SIZE;
 220         session->ns_mover.md_position = 0;
 221 
 222         reply.error = NDMP_NO_ERR;
 223         ndmp_send_reply(connection, (void *) &reply,
 224             "sending mover_listen reply");
 225 }
 226 
 227 
 228 /*
 229  * ndmpd_mover_continue_v2
 230  *
 231  * This handler handles mover_continue requests.
 232  *
 233  * Parameters:
 234  *   connection (input) - connection handle.
 235  *   body       (input) - request message body.
 236  *
 237  * Returns:
 238  *   void
 239  */
 240 /*ARGSUSED*/
 241 void
 242 ndmpd_mover_continue_v2(ndmp_connection_t *connection, void *body)
 243 {
 244         ndmp_mover_continue_reply reply;
 245         ndmpd_session_t *session = ndmp_get_client_data(connection);
 246 
 247         if (session->ns_mover.md_state != NDMP_MOVER_STATE_PAUSED) {
 248 
 249                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 250                 ndmp_send_reply(connection, (void *) &reply,
 251                     "sending mover_continue reply");
 252                 return;
 253         }
 254         session->ns_mover.md_state = NDMP_MOVER_STATE_ACTIVE;
 255         reply.error = NDMP_NO_ERR;
 256         ndmp_send_reply(connection, (void *) &reply,
 257             "sending mover_continue reply");
 258 }
 259 
 260 
 261 /*
 262  * ndmpd_mover_abort_v2
 263  *
 264  * This handler handles mover_abort requests.
 265  *
 266  * Parameters:
 267  *   connection (input) - connection handle.
 268  *   body       (input) - request message body.
 269  *
 270  * Returns:
 271  *   void
 272  */
 273 /*ARGSUSED*/
 274 void
 275 ndmpd_mover_abort_v2(ndmp_connection_t *connection, void *body)
 276 {
 277         ndmp_mover_abort_reply reply;
 278         ndmpd_session_t *session = ndmp_get_client_data(connection);
 279 
 280         if (session->ns_mover.md_state == NDMP_MOVER_STATE_IDLE ||
 281             session->ns_mover.md_state == NDMP_MOVER_STATE_HALTED) {
 282 
 283                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 284                 ndmp_send_reply(connection, (void *) &reply,
 285                     "sending mover_abort reply");
 286                 return;
 287         }
 288 
 289         reply.error = NDMP_NO_ERR;
 290         ndmp_send_reply(connection, (void *) &reply,
 291             "sending mover_abort reply");
 292 
 293         ndmpd_mover_error(session, NDMP_MOVER_HALT_ABORTED);
 294         ndmp_stop_buffer_worker(session);
 295 }
 296 
 297 
 298 /*
 299  * ndmpd_mover_stop_v2
 300  *
 301  * This handler handles mover_stop requests.
 302  *
 303  * Parameters:
 304  *   connection (input) - connection handle.
 305  *   body       (input) - request message body.
 306  *
 307  * Returns:
 308  *   void
 309  */
 310 /*ARGSUSED*/
 311 void
 312 ndmpd_mover_stop_v2(ndmp_connection_t *connection, void *body)
 313 {
 314         ndmp_mover_stop_reply reply;
 315         ndmpd_session_t *session = ndmp_get_client_data(connection);
 316 
 317         if (session->ns_mover.md_state != NDMP_MOVER_STATE_HALTED) {
 318 
 319                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 320                 ndmp_send_reply(connection, (void *) &reply,
 321                     "sending mover_stop reply");
 322                 return;
 323         }
 324 
 325         ndmp_waitfor_op(session);
 326         reply.error = NDMP_NO_ERR;
 327         ndmp_send_reply(connection, (void *) &reply,
 328             "sending mover_stop reply");
 329 
 330         ndmp_lbr_cleanup(session);
 331         ndmpd_mover_cleanup(session);
 332         (void) ndmpd_mover_init(session);
 333         (void) ndmp_lbr_init(session);
 334 }
 335 
 336 
 337 /*
 338  * ndmpd_mover_set_window_v2
 339  *
 340  * This handler handles mover_set_window requests.
 341  *
 342  *
 343  * Parameters:
 344  *   connection (input) - connection handle.
 345  *   body       (input) - request message body.
 346  *
 347  * Returns:
 348  *   void
 349  */
 350 void
 351 ndmpd_mover_set_window_v2(ndmp_connection_t *connection, void *body)
 352 {
 353         ndmp_mover_set_window_request *request;
 354         ndmp_mover_set_window_reply reply;
 355         ndmpd_session_t *session = ndmp_get_client_data(connection);
 356 
 357         request = (ndmp_mover_set_window_request *) body;
 358 
 359         /*
 360          * The NDMPv2 specification states that "a window can be set only
 361          * when in the listen or paused state."
 362          *
 363          * See the comment in ndmpd_mover_set_window_v3 regarding the reason for
 364          * allowing it in the idle state as well.
 365          */
 366         if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE &&
 367             session->ns_mover.md_state != NDMP_MOVER_STATE_PAUSED &&
 368             session->ns_mover.md_state != NDMP_MOVER_STATE_LISTEN) {
 369                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 370         } else {
 371                 if (quad_to_long_long(request->length) == 0) {
 372                         reply.error = NDMP_ILLEGAL_ARGS_ERR;
 373                         syslog(LOG_ERR, "Illegal window size %d",
 374                             quad_to_long_long(request->length));
 375                 } else {
 376                         reply.error = NDMP_NO_ERR;
 377                         session->ns_mover.md_window_offset =
 378                             quad_to_long_long(request->offset);
 379                         session->ns_mover.md_window_length =
 380                             quad_to_long_long(request->length);
 381                         session->ns_mover.md_position =
 382                             session->ns_mover.md_window_offset;
 383                 }
 384         }
 385 
 386         ndmp_send_reply(connection, (void *) &reply,
 387             "sending mover_set_window reply");
 388 }
 389 
 390 
 391 /*
 392  * ndmpd_mover_read_v2
 393  *
 394  * This handler handles mover_read requests. If the requested offset is
 395  * outside of the current window, the mover is paused and a notify_mover_paused
 396  * request is sent notifying the client that a seek is required. If the
 397  * requested offest is within the window but not within the current record,
 398  * then the tape is positioned to the record containing the requested offest.
 399  * The requested amount of data is then read from the tape device and written
 400  * to the data connection.
 401  *
 402  * Parameters:
 403  *   connection (input) - connection handle.
 404  *   body       (input) - request message body.
 405  *
 406  * Returns:
 407  *   void
 408  */
 409 void
 410 ndmpd_mover_read_v2(ndmp_connection_t *connection, void *body)
 411 {
 412         ndmp_mover_read_request *request = (ndmp_mover_read_request *) body;
 413         ndmp_mover_read_reply reply;
 414         ndmpd_session_t *session = ndmp_get_client_data(connection);
 415         int err;
 416 
 417         if (session->ns_mover.md_state != NDMP_MOVER_STATE_ACTIVE ||
 418             session->ns_mover.md_bytes_left_to_read != 0 ||
 419             session->ns_mover.md_mode != NDMP_MOVER_MODE_WRITE) {
 420                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 421                 ndmp_send_reply(connection, &reply,
 422                     "sending mover_read reply");
 423                 return;
 424         }
 425         if (session->ns_tape.td_fd == -1) {
 426                 syslog(LOG_ERR, "Tape device is not open");
 427                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 428                 ndmp_send_reply(connection, &reply,
 429                     "sending mover_read reply");
 430                 return;
 431         }
 432 
 433         reply.error = NDMP_NO_ERR;
 434         ndmp_send_reply(connection, &reply, "sending mover_read reply");
 435 
 436         err = ndmpd_mover_seek(session, quad_to_long_long(request->offset),
 437             quad_to_long_long(request->length));
 438         if (err < 0) {
 439                 ndmpd_mover_error(session, NDMP_MOVER_HALT_INTERNAL_ERROR);
 440                 return;
 441         }
 442         /*
 443          * Just return if we are waiting for the NDMP client to
 444          * complete the seek.
 445          */
 446         if (err == 1)
 447                 return;
 448 
 449         /*
 450          * Start the mover for restore in the 3-way backups.
 451          */
 452         if (start_mover_for_restore(session) < 0)
 453                 ndmpd_mover_error(session, NDMP_MOVER_HALT_INTERNAL_ERROR);
 454 }
 455 
 456 
 457 /*
 458  * ndmpd_mover_close_v2
 459  *
 460  * This handler handles mover_close requests.
 461  *
 462  * Parameters:
 463  *   connection (input) - connection handle.
 464  *   body       (input) - request message body.
 465  *
 466  * Returns:
 467  *   void
 468  */
 469 /*ARGSUSED*/
 470 void
 471 ndmpd_mover_close_v2(ndmp_connection_t *connection, void *body)
 472 {
 473         ndmp_mover_close_reply reply;
 474         ndmpd_session_t *session = ndmp_get_client_data(connection);
 475 
 476         if (session->ns_mover.md_state != NDMP_MOVER_STATE_PAUSED) {
 477 
 478                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 479                 ndmp_send_reply(connection, &reply,
 480                     "sending mover_close reply");
 481                 return;
 482         }
 483         free(session->ns_mover.md_data_addr_v4.tcp_addr_v4);
 484 
 485         reply.error = NDMP_NO_ERR;
 486         ndmp_send_reply(connection, &reply, "sending mover_close reply");
 487 
 488         ndmpd_mover_error(session, NDMP_MOVER_HALT_CONNECT_CLOSED);
 489 }
 490 
 491 
 492 /*
 493  * ndmpd_mover_set_record_size_v2
 494  *
 495  * This handler handles mover_set_record_size requests.
 496  *
 497  * Parameters:
 498  *   connection (input) - connection handle.
 499  *   body       (input) - request message body.
 500  *
 501  * Returns:
 502  *   void
 503  */
 504 void
 505 ndmpd_mover_set_record_size_v2(ndmp_connection_t *connection, void *body)
 506 {
 507         ndmp_mover_set_record_size_request *request;
 508         ndmp_mover_set_record_size_reply reply;
 509 
 510         ndmpd_session_t *session = ndmp_get_client_data(connection);
 511 
 512         request = (ndmp_mover_set_record_size_request *) body;
 513 
 514         session->ns_mover.md_record_size = request->len;
 515         session->ns_mover.md_buf = realloc(session->ns_mover.md_buf,
 516             request->len);
 517 
 518         reply.error = NDMP_NO_ERR;
 519         ndmp_send_reply(connection, &reply,
 520             "sending mover_set_record_size reply");
 521 }
 522 
 523 
 524 /*
 525  * ************************************************************************
 526  * NDMP V3 HANDLERS
 527  * ************************************************************************
 528  */
 529 
 530 /*
 531  * ndmpd_mover_get_state_v3
 532  *
 533  * This handler handles the ndmp_mover_get_state_request.
 534  * Status information for the mover state machine is returned.
 535  *
 536  * Parameters:
 537  *   connection (input) - connection handle.
 538  *   body       (input) - request message body.
 539  *
 540  * Returns:
 541  *   void
 542  */
 543 /*ARGSUSED*/
 544 void
 545 ndmpd_mover_get_state_v3(ndmp_connection_t *connection, void *body)
 546 {
 547         ndmp_mover_get_state_reply_v3 reply;
 548         ndmpd_session_t *session = ndmp_get_client_data(connection);
 549 
 550         (void) memset((void*)&reply, 0, sizeof (reply));
 551 
 552         reply.error = NDMP_NO_ERR;
 553         reply.state = session->ns_mover.md_state;
 554         reply.pause_reason = session->ns_mover.md_pause_reason;
 555         reply.halt_reason = session->ns_mover.md_halt_reason;
 556         reply.record_size = session->ns_mover.md_record_size;
 557         reply.record_num = session->ns_mover.md_record_num;
 558         reply.data_written =
 559             long_long_to_quad(session->ns_mover.md_data_written);
 560         reply.seek_position =
 561             long_long_to_quad(session->ns_mover.md_seek_position);
 562         reply.bytes_left_to_read =
 563             long_long_to_quad(session->ns_mover.md_bytes_left_to_read);
 564         reply.window_offset =
 565             long_long_to_quad(session->ns_mover.md_window_offset);
 566         reply.window_length =
 567             long_long_to_quad(session->ns_mover.md_window_length);
 568         if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE)
 569                 ndmp_copy_addr_v3(&reply.data_connection_addr,
 570                     &session->ns_mover.md_data_addr);
 571 
 572         ndmp_send_reply(connection, &reply,
 573             "sending ndmp_mover_get_state reply");
 574 }
 575 
 576 
 577 /*
 578  * ndmpd_mover_listen_v3
 579  *
 580  * This handler handles ndmp_mover_listen_requests.
 581  * A TCP/IP socket is created that is used to listen for
 582  * and accept data connections initiated by a remote
 583  * data server.
 584  *
 585  * Parameters:
 586  *   connection (input) - connection handle.
 587  *   body       (input) - request message body.
 588  *
 589  * Returns:
 590  *   void
 591  */
 592 void
 593 ndmpd_mover_listen_v3(ndmp_connection_t *connection, void *body)
 594 {
 595         ndmp_mover_listen_request_v3 *request;
 596         ndmp_mover_listen_reply_v3 reply;
 597         ndmpd_session_t *session = ndmp_get_client_data(connection);
 598         ulong_t addr;
 599         ushort_t port;
 600 
 601         request = (ndmp_mover_listen_request_v3 *)body;
 602 
 603         (void) memset((void*)&reply, 0, sizeof (reply));
 604         reply.error = NDMP_NO_ERR;
 605 
 606         if (request->mode != NDMP_MOVER_MODE_READ &&
 607             request->mode != NDMP_MOVER_MODE_WRITE) {
 608                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
 609         } else if (!ndmp_valid_v3addr_type(request->addr_type)) {
 610                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
 611         } else if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE) {
 612                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 613         } else if (session->ns_data.dd_state != NDMP_DATA_STATE_IDLE) {
 614                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 615         } else if (session->ns_tape.td_fd == -1) {
 616                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 617                 syslog(LOG_ERR, "No tape device open");
 618         } else if (request->mode == NDMP_MOVER_MODE_READ &&
 619             session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
 620                 reply.error = NDMP_PERMISSION_ERR;
 621                 syslog(LOG_ERR, "Write protected device.");
 622         }
 623 
 624         if (reply.error != NDMP_NO_ERR) {
 625                 ndmp_send_reply(connection, &reply,
 626                     "error sending ndmp_mover_listen reply");
 627                 return;
 628         }
 629 
 630         switch (request->addr_type) {
 631         case NDMP_ADDR_LOCAL:
 632                 reply.data_connection_addr.addr_type = NDMP_ADDR_LOCAL;
 633                 session->ns_mover.md_data_addr.addr_type = NDMP_ADDR_LOCAL;
 634                 reply.error = NDMP_NO_ERR;
 635                 break;
 636         case NDMP_ADDR_TCP:
 637                 if (create_listen_socket_v3(session, &addr, &port) < 0) {
 638                         reply.error = NDMP_IO_ERR;
 639                         break;
 640                 }
 641                 reply.error = NDMP_NO_ERR;
 642                 reply.data_connection_addr.addr_type = NDMP_ADDR_TCP;
 643                 reply.data_connection_addr.tcp_ip_v3 = htonl(addr);
 644                 reply.data_connection_addr.tcp_port_v3 = htons(port);
 645                 session->ns_mover.md_data_addr.addr_type = NDMP_ADDR_TCP;
 646                 session->ns_mover.md_data_addr.tcp_ip_v3 = addr;
 647                 session->ns_mover.md_data_addr.tcp_port_v3 = ntohs(port);
 648                 syslog(LOG_DEBUG, "listen_socket: %d",
 649                     session->ns_mover.md_listen_sock);
 650                 break;
 651         default:
 652                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
 653         }
 654 
 655         if (reply.error == NDMP_NO_ERR) {
 656                 session->ns_mover.md_mode = request->mode;
 657                 session->ns_mover.md_state = NDMP_MOVER_STATE_LISTEN;
 658         }
 659 
 660         ndmp_send_reply(connection, &reply,
 661             "error sending ndmp_mover_listen reply");
 662 }
 663 
 664 
 665 /*
 666  * ndmpd_mover_continue_v3
 667  *
 668  * This handler handles ndmp_mover_continue_requests.
 669  *
 670  * Parameters:
 671  *   connection (input) - connection handle.
 672  *   body       (input) - request message body.
 673  *
 674  * Returns:
 675  *   void
 676  */
 677 /*ARGSUSED*/
 678 void
 679 ndmpd_mover_continue_v3(ndmp_connection_t *connection, void *body)
 680 {
 681         ndmp_mover_continue_reply reply;
 682         ndmpd_session_t *session = ndmp_get_client_data(connection);
 683         ndmp_lbr_params_t *nlp = ndmp_get_nlp(session);
 684         int ret;
 685 
 686         (void) memset((void*)&reply, 0, sizeof (reply));
 687 
 688         if (session->ns_mover.md_state != NDMP_MOVER_STATE_PAUSED) {
 689                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 690                 ndmp_send_reply(connection, (void *) &reply,
 691                     "sending mover_continue reply");
 692                 return;
 693         }
 694 
 695         if (session->ns_protocol_version == NDMPV4 &&
 696             !session->ns_mover.md_pre_cond) {
 697                 syslog(LOG_DEBUG, "Precondition check");
 698                 reply.error = NDMP_PRECONDITION_ERR;
 699                 ndmp_send_reply(connection, (void *) &reply,
 700                     "sending mover_continue reply");
 701                 return;
 702         }
 703         /*
 704          * Restore the file handler if the mover is remote to the data
 705          * server and the handler was removed pending the continuation of a
 706          * seek request. The handler is removed in mover_data_write().
 707          */
 708         if (session->ns_mover.md_pause_reason == NDMP_MOVER_PAUSE_SEEK &&
 709             session->ns_mover.md_sock != -1) {
 710                 /*
 711                  * If we are here, it means that we needed DMA interference
 712                  * for seek. We should be on the right window, so we do not
 713                  * need the DMA interference anymore.
 714                  * We do another seek inside the Window to move to the
 715                  * exact position on the tape.
 716                  * If the resore is running without DAR the pause reason should
 717                  * not be seek.
 718                  */
 719                 ret = ndmpd_mover_seek(session,
 720                     session->ns_mover.md_seek_position,
 721                     session->ns_mover.md_bytes_left_to_read);
 722                 if (ret < 0) {
 723                         ndmpd_mover_error(session,
 724                             NDMP_MOVER_HALT_INTERNAL_ERROR);
 725                         return;
 726                 }
 727 
 728                 if (!ret) {
 729                         if (ndmpd_add_file_handler(session, (void*) session,
 730                             session->ns_mover.md_sock, NDMPD_SELECT_MODE_WRITE,
 731                             HC_MOVER, mover_data_write_v3) < 0)
 732                                 ndmpd_mover_error(session,
 733                                     NDMP_MOVER_HALT_INTERNAL_ERROR);
 734                 } else {
 735                         /*
 736                          * This should not happen because we should be in the
 737                          * right window. This means that DMA does not follow
 738                          * the V3 spec.
 739                          */
 740                         ndmpd_mover_error(session,
 741                             NDMP_MOVER_HALT_INTERNAL_ERROR);
 742                         return;
 743                 }
 744         }
 745 
 746         (void) mutex_lock(&nlp->nlp_mtx);
 747         session->ns_mover.md_state = NDMP_MOVER_STATE_ACTIVE;
 748         session->ns_mover.md_pause_reason = NDMP_MOVER_PAUSE_NA;
 749         /* The tape has been likely exchanged, reset tape block counter */
 750         session->ns_tape.td_record_count = 0;
 751         (void) cond_broadcast(&nlp->nlp_cv);
 752         (void) mutex_unlock(&nlp->nlp_mtx);
 753 
 754         reply.error = NDMP_NO_ERR;
 755         ndmp_send_reply(connection, (void *) &reply,
 756             "sending mover_continue reply");
 757 }
 758 
 759 
 760 /*
 761  * ndmpd_mover_abort_v3
 762  *
 763  * This handler handles mover_abort requests.
 764  *
 765  * Parameters:
 766  *   connection (input) - connection handle.
 767  *   body       (input) - request message body.
 768  *
 769  * Returns:
 770  *   void
 771  */
 772 /*ARGSUSED*/
 773 void
 774 ndmpd_mover_abort_v3(ndmp_connection_t *connection, void *body)
 775 {
 776         ndmp_mover_abort_reply reply;
 777         ndmpd_session_t *session = ndmp_get_client_data(connection);
 778 
 779         if (session->ns_mover.md_state == NDMP_MOVER_STATE_IDLE ||
 780             session->ns_mover.md_state == NDMP_MOVER_STATE_HALTED) {
 781 
 782                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 783                 ndmp_send_reply(connection, (void *) &reply,
 784                     "sending mover_abort reply");
 785                 return;
 786         }
 787 
 788         reply.error = NDMP_NO_ERR;
 789         ndmp_send_reply(connection, (void *) &reply,
 790             "sending mover_abort reply");
 791 
 792         ndmpd_mover_error(session, NDMP_MOVER_HALT_ABORTED);
 793 }
 794 
 795 
 796 /*
 797  * ndmpd_mover_set_window_v3
 798  *
 799  * This handler handles mover_set_window requests.
 800  *
 801  *
 802  * Parameters:
 803  *   connection (input) - connection handle.
 804  *   body       (input) - request message body.
 805  *
 806  * Returns:
 807  *   void
 808  */
 809 void
 810 ndmpd_mover_set_window_v3(ndmp_connection_t *connection, void *body)
 811 {
 812         ndmp_mover_set_window_request *request;
 813         ndmp_mover_set_window_reply reply;
 814         ndmpd_session_t *session = ndmp_get_client_data(connection);
 815 
 816         request = (ndmp_mover_set_window_request *) body;
 817 
 818         /*
 819          * Note: The spec says that the window can be set only in the listen
 820          * and paused states.  We let this happen when mover is in the idle
 821          * state as well.  I can't rememebr which NDMP client (net_backup 4.5
 822          * or net_worker 6.1.1) forced us to do this!
 823          */
 824         if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE &&
 825             session->ns_mover.md_state != NDMP_MOVER_STATE_LISTEN &&
 826             session->ns_mover.md_state != NDMP_MOVER_STATE_PAUSED) {
 827                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 828         } else if (session->ns_mover.md_record_size == 0) {
 829                 if (session->ns_protocol_version == NDMPV4)
 830                         reply.error = NDMP_PRECONDITION_ERR;
 831                 else
 832                         reply.error = NDMP_ILLEGAL_ARGS_ERR;
 833         } else
 834                 reply.error = NDMP_NO_ERR;
 835 
 836         if (quad_to_long_long(request->length) == 0) {
 837                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
 838         }
 839 
 840         if (reply.error != NDMP_NO_ERR) {
 841                 ndmp_send_reply(connection, (void *) &reply,
 842                     "sending mover_set_window_v3 reply");
 843                 return;
 844         }
 845 
 846         session->ns_mover.md_pre_cond = TRUE;
 847         session->ns_mover.md_window_offset = quad_to_long_long(request->offset);
 848         session->ns_mover.md_window_length = quad_to_long_long(request->length);
 849 
 850         /*
 851          * We have to update the position for DAR. DAR needs this
 852          * information to position to the right index on tape,
 853          * especially when we span the tapes.
 854          */
 855 #ifdef  NO_POSITION_CHANGE
 856         /*
 857          * Do not change the mover position if we are reading from
 858          * the tape.  In this way, we can use the position+window_length
 859          * to know how much we can write to a tape before pausing with
 860          * EOW reason.
 861          */
 862         if (session->ns_mover.md_mode != NDMP_MOVER_MODE_WRITE)
 863 #endif  /* NO_POSITION_CHANGE */
 864                 session->ns_mover.md_position =
 865                     session->ns_mover.md_window_offset;
 866 
 867         ndmp_send_reply(connection, (void *) &reply,
 868             "sending mover_set_window_v3 reply");
 869 }
 870 
 871 
 872 /*
 873  * ndmpd_mover_read_v3
 874  *
 875  * This handler handles ndmp_mover_read_requests.
 876  * If the requested offset is outside of the current window, the mover
 877  * is paused and a notify_mover_paused request is sent notifying the
 878  * client that a seek is required. If the requested offest is within
 879  * the window but not within the current record, then the tape is
 880  * positioned to the record containing the requested offest. The requested
 881  * amount of data is then read from the tape device and written to the
 882  * data connection.
 883  *
 884  * Parameters:
 885  *   connection (input) - connection handle.
 886  *   body       (input) - request message body.
 887  *
 888  * Returns:
 889  *   void
 890  */
 891 void
 892 ndmpd_mover_read_v3(ndmp_connection_t *connection, void *body)
 893 {
 894         ndmp_mover_read_request *request = (ndmp_mover_read_request *)body;
 895         ndmp_mover_read_reply reply;
 896         ndmpd_session_t *session = ndmp_get_client_data(connection);
 897         int err;
 898 
 899         (void) memset((void*)&reply, 0, sizeof (reply));
 900 
 901         if (session->ns_mover.md_state != NDMP_MOVER_STATE_ACTIVE ||
 902             session->ns_mover.md_mode != NDMP_MOVER_MODE_WRITE) {
 903                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 904         } else if (session->ns_mover.md_bytes_left_to_read != 0) {
 905                 reply.error = NDMP_READ_IN_PROGRESS_ERR;
 906         } else if (session->ns_tape.td_fd == -1) {
 907                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 908                 syslog(LOG_ERR, "Tape device is not open");
 909         } else if (quad_to_long_long(request->length) == 0 ||
 910             (quad_to_long_long(request->length) == MAX_WINDOW_SIZE &&
 911             quad_to_long_long(request->offset) != 0)) {
 912                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
 913         } else {
 914                 reply.error = NDMP_NO_ERR;
 915         }
 916 
 917         ndmp_send_reply(connection, (void *) &reply,
 918             "sending ndmp_mover_read_reply");
 919         if (reply.error != NDMP_NO_ERR)
 920                 return;
 921 
 922         err = ndmpd_mover_seek(session, quad_to_long_long(request->offset),
 923             quad_to_long_long(request->length));
 924         if (err < 0) {
 925                 ndmpd_mover_error(session, NDMP_MOVER_HALT_INTERNAL_ERROR);
 926                 return;
 927         }
 928 
 929         /*
 930          * Just return if we are waiting for the DMA to complete the seek.
 931          */
 932         if (err == 1)
 933                 return;
 934 
 935         /*
 936          * Setup a handler function that will be called when
 937          * data can be written to the data connection without blocking.
 938          */
 939         if (ndmpd_add_file_handler(session, (void*)session,
 940             session->ns_mover.md_sock, NDMPD_SELECT_MODE_WRITE, HC_MOVER,
 941             mover_data_write_v3) < 0) {
 942                 ndmpd_mover_error(session, NDMP_MOVER_HALT_INTERNAL_ERROR);
 943                 return;
 944         }
 945 }
 946 
 947 
 948 /*
 949  * ndmpd_mover_set_record_size_v3
 950  *
 951  * This handler handles mover_set_record_size requests.
 952  *
 953  * Parameters:
 954  *   connection (input) - connection handle.
 955  *   body       (input) - request message body.
 956  *
 957  * Returns:
 958  *   void
 959  */
 960 void
 961 ndmpd_mover_set_record_size_v3(ndmp_connection_t *connection, void *body)
 962 {
 963         ndmp_mover_set_record_size_request *request;
 964         ndmp_mover_set_record_size_reply reply;
 965         ndmpd_session_t *session = ndmp_get_client_data(connection);
 966         char *cp;
 967 
 968         request = (ndmp_mover_set_record_size_request *) body;
 969 
 970         if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE) {
 971                 reply.error = NDMP_ILLEGAL_STATE_ERR;
 972         } else if (request->len > (unsigned int)ndmp_max_mover_recsize) {
 973                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
 974         } else if (request->len == session->ns_mover.md_record_size) {
 975                 reply.error = NDMP_NO_ERR;
 976                 session->ns_mover.md_pre_cond = TRUE;
 977         } else if (!(cp = realloc(session->ns_mover.md_buf, request->len))) {
 978                 reply.error = NDMP_NO_MEM_ERR;
 979         } else {
 980                 reply.error = NDMP_NO_ERR;
 981                 session->ns_mover.md_buf = cp;
 982                 session->ns_mover.md_record_size = request->len;
 983                 session->ns_mover.md_window_offset = 0;
 984                 session->ns_mover.md_window_length = 0;
 985         }
 986 
 987         ndmp_send_reply(connection, (void *) &reply,
 988             "sending mover_set_record_size reply");
 989 }
 990 
 991 
 992 /*
 993  * ndmpd_mover_connect_v3
 994  *   Request handler. Connects the mover to either a local
 995  *   or remote data server.
 996  *
 997  * Parameters:
 998  *   connection (input) - connection handle.
 999  *   body       (input) - request message body.
1000  *
1001  * Returns:
1002  *   void
1003  */
1004 void
1005 ndmpd_mover_connect_v3(ndmp_connection_t *connection, void *body)
1006 {
1007         ndmp_mover_connect_request_v3 *request;
1008         ndmp_mover_connect_reply_v3 reply;
1009         ndmpd_session_t *session = ndmp_get_client_data(connection);
1010 
1011         request = (ndmp_mover_connect_request_v3*)body;
1012 
1013         (void) memset((void*)&reply, 0, sizeof (reply));
1014 
1015         if (request->mode != NDMP_MOVER_MODE_READ &&
1016             request->mode != NDMP_MOVER_MODE_WRITE) {
1017                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1018         } else if (!ndmp_valid_v3addr_type(request->addr.addr_type)) {
1019                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1020         } else if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE) {
1021                 reply.error = NDMP_ILLEGAL_STATE_ERR;
1022         } else if (session->ns_tape.td_fd == -1) {
1023                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
1024                 syslog(LOG_ERR, "No tape device open");
1025         } else if (request->mode == NDMP_MOVER_MODE_READ &&
1026             session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
1027                 reply.error = NDMP_WRITE_PROTECT_ERR;
1028                 syslog(LOG_ERR, "Write protected device.");
1029         } else
1030                 reply.error = NDMP_NO_ERR;
1031 
1032         if (reply.error != NDMP_NO_ERR) {
1033                 ndmp_send_reply(connection, (void *) &reply,
1034                     "sending ndmp_mover_connect reply");
1035                 return;
1036         }
1037 
1038         switch (request->addr.addr_type) {
1039         case NDMP_ADDR_LOCAL:
1040                 /*
1041                  * Verify that the data server is listening for a
1042                  * local connection.
1043                  */
1044                 if (session->ns_data.dd_state != NDMP_DATA_STATE_LISTEN ||
1045                     session->ns_data.dd_listen_sock != -1) {
1046                         reply.error = NDMP_ILLEGAL_STATE_ERR;
1047                 } else
1048                         session->ns_data.dd_state = NDMP_DATA_STATE_CONNECTED;
1049                 break;
1050 
1051         case NDMP_ADDR_TCP:
1052                 reply.error = mover_connect_sock(session, request->mode,
1053                     request->addr.tcp_ip_v3, request->addr.tcp_port_v3);
1054                 break;
1055 
1056         default:
1057                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1058         }
1059 
1060         if (reply.error == NDMP_NO_ERR) {
1061                 session->ns_mover.md_data_addr.addr_type =
1062                     request->addr.addr_type;
1063                 session->ns_mover.md_state = NDMP_MOVER_STATE_ACTIVE;
1064                 session->ns_mover.md_mode = request->mode;
1065         }
1066 
1067         ndmp_send_reply(connection, (void *) &reply,
1068             "sending ndmp_mover_connect reply");
1069 }
1070 
1071 
1072 /*
1073  * ************************************************************************
1074  * NDMP V4 HANDLERS
1075  * ************************************************************************
1076  */
1077 
1078 /*
1079  * ndmpd_mover_get_state_v4
1080  *
1081  * This handler handles the ndmp_mover_get_state_request.
1082  * Status information for the mover state machine is returned.
1083  *
1084  * Parameters:
1085  *   connection (input) - connection handle.
1086  *   body       (input) - request message body.
1087  *
1088  * Returns:
1089  *   void
1090  */
1091 /*ARGSUSED*/
1092 void
1093 ndmpd_mover_get_state_v4(ndmp_connection_t *connection, void *body)
1094 {
1095         ndmp_mover_get_state_reply_v4 reply;
1096         ndmpd_session_t *session = ndmp_get_client_data(connection);
1097 
1098         (void) memset((void*)&reply, 0, sizeof (reply));
1099 
1100         reply.error = NDMP_NO_ERR;
1101         reply.state = session->ns_mover.md_state;
1102         reply.mode = session->ns_mover.md_mode;
1103         reply.pause_reason = session->ns_mover.md_pause_reason;
1104         reply.halt_reason = session->ns_mover.md_halt_reason;
1105         reply.record_size = session->ns_mover.md_record_size;
1106         reply.record_num = session->ns_mover.md_record_num;
1107         reply.bytes_moved =
1108             long_long_to_quad(session->ns_mover.md_data_written);
1109         reply.seek_position =
1110             long_long_to_quad(session->ns_mover.md_seek_position);
1111         reply.bytes_left_to_read =
1112             long_long_to_quad(session->ns_mover.md_bytes_left_to_read);
1113         reply.window_offset =
1114             long_long_to_quad(session->ns_mover.md_window_offset);
1115         reply.window_length =
1116             long_long_to_quad(session->ns_mover.md_window_length);
1117         if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE)
1118                 ndmp_copy_addr_v4(&reply.data_connection_addr,
1119                     &session->ns_mover.md_data_addr_v4);
1120 
1121         ndmp_send_reply(connection, (void *) &reply,
1122             "sending ndmp_mover_get_state reply");
1123         free(reply.data_connection_addr.tcp_addr_v4);
1124 }
1125 
1126 
1127 /*
1128  * ndmpd_mover_listen_v4
1129  *
1130  * This handler handles ndmp_mover_listen_requests.
1131  * A TCP/IP socket is created that is used to listen for
1132  * and accept data connections initiated by a remote
1133  * data server.
1134  *
1135  * Parameters:
1136  *   connection (input) - connection handle.
1137  *   body       (input) - request message body.
1138  *
1139  * Returns:
1140  *   void
1141  */
1142 void
1143 ndmpd_mover_listen_v4(ndmp_connection_t *connection, void *body)
1144 {
1145         ndmp_mover_listen_request_v4 *request;
1146 
1147         ndmp_mover_listen_reply_v4 reply;
1148         ndmpd_session_t *session = ndmp_get_client_data(connection);
1149         ulong_t addr;
1150         ushort_t port;
1151 
1152         request = (ndmp_mover_listen_request_v4 *)body;
1153 
1154         (void) memset((void*)&reply, 0, sizeof (reply));
1155         reply.error = NDMP_NO_ERR;
1156 
1157         if (request->mode != NDMP_MOVER_MODE_READ &&
1158             request->mode != NDMP_MOVER_MODE_WRITE) {
1159                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1160         } else if (!ndmp_valid_v3addr_type(request->addr_type)) {
1161                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1162         } else if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE) {
1163                 reply.error = NDMP_ILLEGAL_STATE_ERR;
1164         } else if (session->ns_data.dd_state != NDMP_DATA_STATE_IDLE) {
1165                 reply.error = NDMP_ILLEGAL_STATE_ERR;
1166         } else if (session->ns_tape.td_fd == -1) {
1167                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
1168                 syslog(LOG_ERR, "No tape device open");
1169         } else if (session->ns_mover.md_record_size == 0) {
1170                 reply.error = NDMP_PRECONDITION_ERR;
1171         } else if (request->mode == NDMP_MOVER_MODE_READ &&
1172             session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
1173                 reply.error = NDMP_PERMISSION_ERR;
1174                 syslog(LOG_ERR, "Write protected device.");
1175         }
1176 
1177         if (reply.error != NDMP_NO_ERR) {
1178                 ndmp_send_reply(connection, (void *) &reply,
1179                     "error sending ndmp_mover_listen reply");
1180                 return;
1181         }
1182 
1183         switch (request->addr_type) {
1184         case NDMP_ADDR_LOCAL:
1185                 reply.connect_addr.addr_type = NDMP_ADDR_LOCAL;
1186                 session->ns_mover.md_data_addr.addr_type = NDMP_ADDR_LOCAL;
1187                 reply.error = NDMP_NO_ERR;
1188                 break;
1189         case NDMP_ADDR_TCP:
1190                 if (create_listen_socket_v3(session, &addr, &port) < 0) {
1191                         reply.error = NDMP_IO_ERR;
1192                         break;
1193                 }
1194                 reply.error = NDMP_NO_ERR;
1195 
1196                 session->ns_mover.md_data_addr_v4.addr_type = NDMP_ADDR_TCP;
1197                 session->ns_mover.md_data_addr_v4.tcp_len_v4 = 1;
1198                 session->ns_mover.md_data_addr_v4.tcp_addr_v4 =
1199                     ndmp_malloc(sizeof (ndmp_tcp_addr_v4));
1200 
1201                 session->ns_mover.md_data_addr_v4.tcp_ip_v4(0) = addr;
1202                 session->ns_mover.md_data_addr_v4.tcp_port_v4(0) = ntohs(port);
1203 
1204                 ndmp_copy_addr_v4(&reply.connect_addr,
1205                     &session->ns_mover.md_data_addr_v4);
1206 
1207                 /* For compatibility with V3 */
1208                 session->ns_mover.md_data_addr.addr_type = NDMP_ADDR_TCP;
1209                 session->ns_mover.md_data_addr.tcp_ip_v3 = addr;
1210                 session->ns_mover.md_data_addr.tcp_port_v3 = ntohs(port);
1211                 syslog(LOG_DEBUG, "listen_socket: %d",
1212                     session->ns_mover.md_listen_sock);
1213                 break;
1214         default:
1215                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1216         }
1217 
1218         if (reply.error == NDMP_NO_ERR) {
1219                 session->ns_mover.md_mode = request->mode;
1220                 session->ns_mover.md_state = NDMP_MOVER_STATE_LISTEN;
1221         }
1222 
1223         ndmp_send_reply(connection, (void *) &reply,
1224             "error sending ndmp_mover_listen reply");
1225         free(reply.connect_addr.tcp_addr_v4);
1226 }
1227 
1228 /*
1229  * ndmpd_mover_connect_v4
1230  *   Request handler. Connects the mover to either a local
1231  *   or remote data server.
1232  *
1233  * Parameters:
1234  *   connection (input) - connection handle.
1235  *   body       (input) - request message body.
1236  *
1237  * Returns:
1238  *   void
1239  */
1240 void
1241 ndmpd_mover_connect_v4(ndmp_connection_t *connection, void *body)
1242 {
1243         ndmp_mover_connect_request_v4 *request;
1244         ndmp_mover_connect_reply_v4 reply;
1245         ndmpd_session_t *session = ndmp_get_client_data(connection);
1246 
1247         request = (ndmp_mover_connect_request_v4 *)body;
1248         (void) memset((void*)&reply, 0, sizeof (reply));
1249 
1250         if (request->mode != NDMP_MOVER_MODE_READ &&
1251             request->mode != NDMP_MOVER_MODE_WRITE) {
1252                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1253         } else if (!ndmp_valid_v3addr_type(request->addr.addr_type)) {
1254                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1255         } else if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE) {
1256                 reply.error = NDMP_ILLEGAL_STATE_ERR;
1257         } else if (session->ns_tape.td_fd == -1) {
1258                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
1259                 syslog(LOG_ERR, "No tape device open");
1260         } else if (request->mode == NDMP_MOVER_MODE_READ &&
1261             session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
1262                 reply.error = NDMP_PERMISSION_ERR;
1263                 syslog(LOG_ERR, "Write protected device.");
1264         } else if (session->ns_mover.md_record_size == 0) {
1265                 reply.error = NDMP_PRECONDITION_ERR;
1266         } else
1267                 reply.error = NDMP_NO_ERR;
1268 
1269         if (reply.error != NDMP_NO_ERR) {
1270                 ndmp_send_reply(connection, (void *) &reply,
1271                     "sending ndmp_mover_connect reply");
1272                 return;
1273         }
1274 
1275         switch (request->addr.addr_type) {
1276         case NDMP_ADDR_LOCAL:
1277                 /*
1278                  * Verify that the data server is listening for a
1279                  * local connection.
1280                  */
1281                 if (session->ns_data.dd_state != NDMP_DATA_STATE_LISTEN ||
1282                     session->ns_data.dd_listen_sock != -1) {
1283                         reply.error = NDMP_ILLEGAL_STATE_ERR;
1284                 } else
1285                         session->ns_data.dd_state = NDMP_DATA_STATE_CONNECTED;
1286                 break;
1287 
1288         case NDMP_ADDR_TCP:
1289                 reply.error = mover_connect_sock(session, request->mode,
1290                     request->addr.tcp_ip_v4(0), request->addr.tcp_port_v4(0));
1291                 break;
1292 
1293         default:
1294                 reply.error = NDMP_ILLEGAL_ARGS_ERR;
1295         }
1296 
1297         if (reply.error == NDMP_NO_ERR) {
1298                 session->ns_mover.md_data_addr.addr_type =
1299                     request->addr.addr_type;
1300                 session->ns_mover.md_state = NDMP_MOVER_STATE_ACTIVE;
1301                 session->ns_mover.md_mode = request->mode;
1302         }
1303 
1304         ndmp_send_reply(connection, (void *) &reply,
1305             "sending ndmp_mover_connect reply");
1306 }
1307 
1308 
1309 
1310 /*
1311  * ************************************************************************
1312  * LOCALS
1313  * ************************************************************************
1314  */
1315 
1316 /*
1317  * ndmpd_local_write
1318  *
1319  * Writes data to the mover.
1320  * Buffers and write data to the tape device.
1321  * A full tape record is buffered before being written.
1322  *
1323  * Parameters:
1324  *   session    (input) - session pointer.
1325  *   data       (input) - data to be written.
1326  *   length     (input) - data length.
1327  *
1328  * Returns:
1329  *   0 - data successfully written.
1330  *  -1 - error.
1331  */
1332 int
1333 ndmpd_local_write(ndmpd_session_t *session, char *data, ulong_t length)
1334 {
1335         ulong_t count = 0;
1336         ssize_t n;
1337         ulong_t len;
1338 
1339         /*
1340          * A length of 0 indicates that any buffered data should be
1341          * flushed to tape.
1342          */
1343         if (length == 0) {
1344                 if (session->ns_mover.md_w_index == 0)
1345                         return (0);
1346 
1347                 (void) memset(
1348                     &session->ns_mover.md_buf[session->ns_mover.md_w_index],
1349                     0, session->ns_mover.md_record_size -
1350                     session->ns_mover.md_w_index);
1351 
1352                 n = mover_tape_write_v3(session, session->ns_mover.md_buf,
1353                     session->ns_mover.md_record_size);
1354                 if (n <= 0) {
1355                         ndmpd_mover_error(session,
1356                             (n == 0 ? NDMP_MOVER_HALT_ABORTED :
1357                             NDMP_MOVER_HALT_INTERNAL_ERROR));
1358                         return (-1);
1359                 }
1360                 session->ns_mover.md_position += n;
1361                 session->ns_mover.md_data_written +=
1362                     session->ns_mover.md_w_index;
1363                 session->ns_mover.md_record_num++;
1364                 session->ns_mover.md_w_index = 0;
1365                 return (0);
1366         }
1367         /* Break the data into records. */
1368         while (count < length) {
1369                 /*
1370                  * Determine if data needs to be buffered or
1371                  * can be written directly from user supplied location.
1372                  * We can fast path the write if there is no pending
1373                  * buffered data and there is at least a full record's worth
1374                  * of data to be written.
1375                  */
1376                 if (session->ns_mover.md_w_index == 0 &&
1377                     length - count >= session->ns_mover.md_record_size) {
1378                         n = mover_tape_write_v3(session, &data[count],
1379                             session->ns_mover.md_record_size);
1380                         if (n <= 0) {
1381                                 ndmpd_mover_error(session,
1382                                     (n == 0 ? NDMP_MOVER_HALT_ABORTED :
1383                                     NDMP_MOVER_HALT_INTERNAL_ERROR));
1384                                 return (-1);
1385                         }
1386                         session->ns_mover.md_position += n;
1387                         session->ns_mover.md_data_written += n;
1388                         session->ns_mover.md_record_num++;
1389                         count += n;
1390                         continue;
1391                 }
1392                 /* Buffer the data */
1393                 len = length - count;
1394                 if (len > session->ns_mover.md_record_size -
1395                     session->ns_mover.md_w_index)
1396                         len = session->ns_mover.md_record_size -
1397                             session->ns_mover.md_w_index;
1398 
1399                 (void) memcpy(
1400                     &session->ns_mover.md_buf[session->ns_mover.md_w_index],
1401                     &data[count], len);
1402                 session->ns_mover.md_w_index += len;
1403                 count += len;
1404 
1405                 /* Write the buffer if its full */
1406                 if (session->ns_mover.md_w_index ==
1407                     session->ns_mover.md_record_size) {
1408                         n = mover_tape_write_v3(session,
1409                             session->ns_mover.md_buf,
1410                             session->ns_mover.md_record_size);
1411                         if (n <= 0) {
1412                                 ndmpd_mover_error(session,
1413                                     (n == 0 ? NDMP_MOVER_HALT_ABORTED :
1414                                     NDMP_MOVER_HALT_INTERNAL_ERROR));
1415                                 return (-1);
1416                         }
1417                         session->ns_mover.md_position += n;
1418                         session->ns_mover.md_data_written += n;
1419                         session->ns_mover.md_record_num++;
1420                         session->ns_mover.md_w_index = 0;
1421                 }
1422         }
1423 
1424         return (0);
1425 }
1426 
1427 
1428 /*
1429  * ndmpd_remote_write
1430  *
1431  * Writes data to the remote mover.
1432  *
1433  * Parameters:
1434  *   session    (input) - session pointer.
1435  *   data       (input) - data to be written.
1436  *   length     (input) - data length.
1437  *
1438  * Returns:
1439  *   0 - data successfully written.
1440  *  -1 - error.
1441  */
1442 int
1443 ndmpd_remote_write(ndmpd_session_t *session, char *data, ulong_t length)
1444 {
1445         ssize_t n;
1446         ulong_t count = 0;
1447 
1448         while (count < length) {
1449                 if (session->ns_eof == TRUE ||
1450                     session->ns_data.dd_abort == TRUE)
1451                         return (-1);
1452 
1453                 if ((n = write(session->ns_data.dd_sock, &data[count],
1454                     length - count)) < 0) {
1455                         syslog(LOG_ERR, "Socket write error: %m.");
1456                         return (-1);
1457                 }
1458                 count += n;
1459         }
1460 
1461         return (0);
1462 }
1463 
1464 /*
1465  * ndmpd_local_read
1466  *
1467  * Reads data from the local tape device.
1468  * Full tape records are read and buffered.
1469  *
1470  * Parameters:
1471  *   session (input) - session pointer.
1472  *   data    (input) - location to store data.
1473  *   length  (input) - data length.
1474  *
1475  * Returns:
1476  *   0 - data successfully read.
1477  *  -1 - error.
1478  *   1 - session terminated or operation aborted.
1479  */
1480 int
1481 ndmpd_local_read(ndmpd_session_t *session, char *data, ulong_t length)
1482 {
1483         ulong_t count = 0;
1484         ssize_t n;
1485         ulong_t len;
1486         ndmp_notify_mover_paused_request pause_request;
1487 
1488         /*
1489          * Automatically increase the seek window if necessary.
1490          * This is needed in the event the module attempts to read
1491          * past a seek window set via a prior call to ndmpd_seek() or
1492          * the module has not issued a seek. If no seek was issued then
1493          * pretend that a seek was issued to read the entire tape.
1494          */
1495         if (length > session->ns_mover.md_bytes_left_to_read) {
1496                 /* ndmpd_seek() never called? */
1497                 if (session->ns_data.dd_read_length == 0) {
1498                         session->ns_mover.md_bytes_left_to_read = ~0LL;
1499                         session->ns_data.dd_read_offset = 0LL;
1500                         session->ns_data.dd_read_length = ~0LL;
1501                 } else {
1502                         session->ns_mover.md_bytes_left_to_read = length;
1503                         session->ns_data.dd_read_offset =
1504                             session->ns_mover.md_position;
1505                         session->ns_data.dd_read_length = length;
1506                 }
1507         }
1508         /*
1509          * Read as many records as necessary to satisfy the request.
1510          */
1511         while (count < length) {
1512                 /*
1513                  * If the end of the mover window has been reached,
1514                  * then notify the client that a new data window is needed.
1515                  */
1516                 if (session->ns_mover.md_position >=
1517                     session->ns_mover.md_window_offset +
1518                     session->ns_mover.md_window_length) {
1519 
1520                         session->ns_mover.md_state = NDMP_MOVER_STATE_PAUSED;
1521                         session->ns_mover.md_pause_reason =
1522                             NDMP_MOVER_PAUSE_SEEK;
1523                         pause_request.reason = NDMP_MOVER_PAUSE_SEEK;
1524                         pause_request.seek_position =
1525                             long_long_to_quad(session->ns_mover.md_position);
1526 
1527                         if (ndmp_send_request(session->ns_connection,
1528                             NDMP_NOTIFY_MOVER_PAUSED, NDMP_NO_ERR,
1529                             (void *) &pause_request, 0) < 0) {
1530                                 ndmpd_mover_error(session,
1531                                     NDMP_MOVER_HALT_INTERNAL_ERROR);
1532                                 return (-1);
1533                         }
1534                         /*
1535                          * Wait until the state is changed by
1536                          * an abort or continue request.
1537                          */
1538                         if (ndmp_wait_for_mover(session) != 0)
1539                                 return (1);
1540                 }
1541                 len = length - count;
1542 
1543                 /*
1544                  * Prevent reading past the end of the window.
1545                  */
1546                 if (len >
1547                     session->ns_mover.md_window_offset +
1548                     session->ns_mover.md_window_length -
1549                     session->ns_mover.md_position)
1550                         len = session->ns_mover.md_window_offset +
1551                             session->ns_mover.md_window_length -
1552                             session->ns_mover.md_position;
1553 
1554                 /*
1555                  * Copy from the data buffer first.
1556                  */
1557                 if (session->ns_mover.md_w_index -
1558                     session->ns_mover.md_r_index != 0) {
1559                         /*
1560                          * Limit the copy to the amount of data in the buffer.
1561                          */
1562                         if (len > session->ns_mover.md_w_index -
1563                             session->ns_mover.md_r_index)
1564                                 len = session->ns_mover.md_w_index
1565                                     - session->ns_mover.md_r_index;
1566 
1567                         (void) memcpy((void *) &data[count],
1568                             &session->ns_mover.md_buf[session->
1569                             ns_mover.md_r_index], len);
1570                         count += len;
1571                         session->ns_mover.md_r_index += len;
1572                         session->ns_mover.md_bytes_left_to_read -= len;
1573                         session->ns_mover.md_position += len;
1574                         continue;
1575                 }
1576                 /*
1577                  * Determine if data needs to be buffered or
1578                  * can be read directly to user supplied location.
1579                  * We can fast path the read if at least a full record
1580                  * needs to be read and there is no seek pending.
1581                  * This is done to eliminate a buffer copy.
1582                  */
1583                 if (len >= session->ns_mover.md_record_size &&
1584                     session->ns_mover.md_position >=
1585                     session->ns_mover.md_seek_position) {
1586                         n = tape_read(session, &data[count]);
1587                         if (n <= 0) {
1588                                 if (n == TAPE_NO_WRITER_ERR)
1589                                         return (1);
1590 
1591                                 ndmpd_mover_error(session,
1592                                     (n == 0 ? NDMP_MOVER_HALT_ABORTED :
1593                                     NDMP_MOVER_HALT_INTERNAL_ERROR));
1594                                 return (n == 0) ? (1) : (-1);
1595                         }
1596                         count += n;
1597                         session->ns_mover.md_bytes_left_to_read -= n;
1598                         session->ns_mover.md_position += n;
1599                         continue;
1600                 }
1601                 /* Read the next record into the buffer. */
1602                 n = tape_read(session, session->ns_mover.md_buf);
1603                 if (n <= 0) {
1604                         if (n == TAPE_NO_WRITER_ERR)
1605                                 return (1);
1606 
1607                         ndmpd_mover_error(session,
1608                             (n == 0 ? NDMP_MOVER_HALT_ABORTED :
1609                             NDMP_MOVER_HALT_INTERNAL_ERROR));
1610                         return (n == 0) ? (1) : (-1);
1611                 }
1612                 session->ns_mover.md_w_index = n;
1613                 session->ns_mover.md_r_index = 0;
1614 
1615                 /*
1616                  * Discard data if the current data stream position is
1617                  * prior to the seek position. This is necessary if a seek
1618                  * request set the seek pointer to a position that is not a
1619                  * record boundary. The seek request handler can only position
1620                  * to the start of a record.
1621                  */
1622                 if (session->ns_mover.md_position <
1623                     session->ns_mover.md_seek_position) {
1624                         session->ns_mover.md_r_index =
1625                             session->ns_mover.md_seek_position -
1626                             session->ns_mover.md_position;
1627                         session->ns_mover.md_position =
1628                             session->ns_mover.md_seek_position;
1629                 }
1630         }
1631 
1632         return (0);
1633 }
1634 
1635 
1636 /*
1637  * ndmpd_remote_read
1638  *
1639  * Reads data from the remote mover.
1640  *
1641  * Parameters:
1642  *   session (input) - session pointer.
1643  *   data    (input) - data to be written.
1644  *   length  (input) - data length.
1645  *
1646  * Returns:
1647  *   0 - data successfully read.
1648  *  -1 - error.
1649  *   1 - session terminated or operation aborted.
1650  */
1651 int
1652 ndmpd_remote_read(ndmpd_session_t *session, char *data, ulong_t length)
1653 {
1654         ulong_t count = 0;
1655         ssize_t n;
1656         ulong_t len;
1657         ndmp_notify_data_read_request request;
1658 
1659         while (count < length) {
1660                 len = length - count;
1661 
1662                 /*
1663                  * If the end of the seek window has been reached then
1664                  * send an ndmp_read request to the client.
1665                  * The NDMP client will then send a mover_data_read request to
1666                  * the remote mover and the mover will send more data.
1667                  * This condition can occur if the module attempts to read past
1668                  * a seek window set via a prior call to ndmpd_seek() or
1669                  * the module has not issued a seek. If no seek was issued then
1670                  * pretend that a seek was issued to read the entire tape.
1671                  */
1672                 if (session->ns_mover.md_bytes_left_to_read == 0) {
1673                         /* ndmpd_seek() never called? */
1674                         if (session->ns_data.dd_read_length == 0) {
1675                                 session->ns_mover.md_bytes_left_to_read = ~0LL;
1676                                 session->ns_data.dd_read_offset = 0LL;
1677                                 session->ns_data.dd_read_length = ~0LL;
1678                         } else {
1679                                 session->ns_mover.md_bytes_left_to_read = len;
1680                                 session->ns_data.dd_read_offset =
1681                                     session->ns_mover.md_position;
1682                                 session->ns_data.dd_read_length = len;
1683                         }
1684 
1685                         request.offset =
1686                             long_long_to_quad(session->ns_data.dd_read_offset);
1687                         request.length =
1688                             long_long_to_quad(session->ns_data.dd_read_length);
1689 
1690                         if (ndmp_send_request_lock(session->ns_connection,
1691                             NDMP_NOTIFY_DATA_READ, NDMP_NO_ERR,
1692                             (void *) &request, 0) < 0) {
1693                                 return (-1);
1694                         }
1695                 }
1696                 if (session->ns_eof == TRUE ||
1697                     session->ns_data.dd_abort == TRUE)
1698                         return (1);
1699 
1700                 /*
1701                  * If the module called ndmpd_seek() prior to reading all of the
1702                  * data that the remote mover was requested to send, then the
1703                  * excess data from the seek has to be discardd.
1704                  */
1705                 if (session->ns_mover.md_discard_length != 0) {
1706                         n = discard_data(session,
1707                             (ulong_t)session->ns_mover.md_discard_length);
1708                         if (n < 0)
1709                                 return (-1);
1710                         session->ns_mover.md_discard_length -= n;
1711                         continue;
1712                 }
1713                 /*
1714                  * Don't attempt to read more data than the remote is sending.
1715                  */
1716                 if (len > session->ns_mover.md_bytes_left_to_read)
1717                         len = session->ns_mover.md_bytes_left_to_read;
1718 
1719                 if ((n = read(session->ns_data.dd_sock, &data[count],
1720                     len)) < 0) {
1721                         syslog(LOG_ERR, "Socket read error: %m.");
1722                         return (-1);
1723                 }
1724                 /* read returns 0 if the connection was closed */
1725                 if (n == 0)
1726                         return (-1);
1727 
1728                 count += n;
1729                 session->ns_mover.md_bytes_left_to_read -= n;
1730                 session->ns_mover.md_position += n;
1731         }
1732 
1733         return (0);
1734 }
1735 
1736 /* *** ndmpd internal functions ***************************************** */
1737 
1738 /*
1739  * ndmpd_mover_init
1740  *
1741  * Initialize mover specific session variables.
1742  * Don't initialize variables such as record_size that need to
1743  * persist across data operations. A client may open a connection and
1744  * do multiple backups after setting the record_size.
1745  *
1746  * Parameters:
1747  *   session (input) - session pointer.
1748  *
1749  * Returns:
1750  *   0 - success.
1751  *  -1 - error.
1752  */
1753 int
1754 ndmpd_mover_init(ndmpd_session_t *session)
1755 {
1756         session->ns_mover.md_state = NDMP_MOVER_STATE_IDLE;
1757         session->ns_mover.md_pause_reason = NDMP_MOVER_PAUSE_NA;
1758         session->ns_mover.md_halt_reason = NDMP_MOVER_HALT_NA;
1759         session->ns_mover.md_data_written = 0LL;
1760         session->ns_mover.md_seek_position = 0LL;
1761         session->ns_mover.md_bytes_left_to_read = 0LL;
1762         session->ns_mover.md_window_offset = 0LL;
1763         session->ns_mover.md_window_length = MAX_WINDOW_SIZE;
1764         session->ns_mover.md_position = 0LL;
1765         session->ns_mover.md_discard_length = 0;
1766         session->ns_mover.md_record_num = 0;
1767         session->ns_mover.md_record_size = 0;
1768         session->ns_mover.md_listen_sock = -1;
1769         session->ns_mover.md_pre_cond = FALSE;
1770         session->ns_mover.md_sock = -1;
1771         session->ns_mover.md_r_index = 0;
1772         session->ns_mover.md_w_index = 0;
1773         session->ns_mover.md_buf = ndmp_malloc(MAX_RECORD_SIZE);
1774         if (!session->ns_mover.md_buf)
1775                 return (-1);
1776 
1777         if (ndmp_get_version(session->ns_connection) == NDMPV3) {
1778                 session->ns_mover.md_mode = NDMP_MOVER_MODE_READ;
1779                 (void) memset(&session->ns_mover.md_data_addr, 0,
1780                     sizeof (ndmp_addr_v3));
1781         }
1782         return (0);
1783 }
1784 
1785 
1786 /*
1787  * ndmpd_mover_shut_down
1788  *
1789  * Shutdown the mover. It closes all the sockets.
1790  *
1791  * Parameters:
1792  *   session (input) - session pointer.
1793  *
1794  * Returns:
1795  *   void
1796  */
1797 void
1798 ndmpd_mover_shut_down(ndmpd_session_t *session)
1799 {
1800         ndmp_lbr_params_t *nlp;
1801 
1802         if ((nlp = ndmp_get_nlp(session)) == NULL)
1803                 return;
1804 
1805         (void) mutex_lock(&nlp->nlp_mtx);
1806         if (session->ns_mover.md_listen_sock != -1) {
1807                 (void) ndmpd_remove_file_handler(session,
1808                     session->ns_mover.md_listen_sock);
1809                 (void) close(session->ns_mover.md_listen_sock);
1810                 session->ns_mover.md_listen_sock = -1;
1811         }
1812         if (session->ns_mover.md_sock != -1) {
1813                 (void) ndmpd_remove_file_handler(session,
1814                     session->ns_mover.md_sock);
1815                 (void) close(session->ns_mover.md_sock);
1816                 session->ns_mover.md_sock = -1;
1817         }
1818         (void) cond_broadcast(&nlp->nlp_cv);
1819         (void) mutex_unlock(&nlp->nlp_mtx);
1820 }
1821 
1822 
1823 /*
1824  * ndmpd_mover_cleanup
1825  *
1826  * Parameters:
1827  *   session (input) - session pointer.
1828  *
1829  * Returns:
1830  *   void
1831  */
1832 void
1833 ndmpd_mover_cleanup(ndmpd_session_t *session)
1834 {
1835         NDMP_FREE(session->ns_mover.md_buf);
1836 }
1837 
1838 
1839 /*
1840  * ndmpd_mover_connect
1841  *   Create a connection to the specified mover.
1842  *
1843  * Parameters:
1844  *   session (input) - session pointer
1845  *
1846  * Returns:
1847  *   error code.
1848  */
1849 ndmp_error
1850 ndmpd_mover_connect(ndmpd_session_t *session, ndmp_mover_mode mover_mode)
1851 {
1852         ndmp_mover_addr *mover = &session->ns_data.dd_mover;
1853         struct sockaddr_in sin;
1854         int sock = -1;
1855 
1856         if (mover->addr_type == NDMP_ADDR_TCP) {
1857                 if (mover->ndmp_mover_addr_u.addr.ip_addr) {
1858                         (void) memset((void *) &sin, 0, sizeof (sin));
1859                         sin.sin_family = AF_INET;
1860                         sin.sin_addr.s_addr =
1861                             htonl(mover->ndmp_mover_addr_u.addr.ip_addr);
1862                         sin.sin_port =
1863                             htons(mover->ndmp_mover_addr_u.addr.port);
1864 
1865                         /*
1866                          * If the address type is TCP but both the address and
1867                          * the port number are zero, we have to use a different
1868                          * socket than the mover socket. This can happen when
1869                          * using NDMP disk to disk copy (AKA D2D copy).
1870                          * The NDMPCopy client will send a zero address to
1871                          * direct the server to use the mover socket as the
1872                          * data socket to receive the recovery data.
1873                          */
1874                         if (sin.sin_addr.s_addr == 0 && sin.sin_port == 0) {
1875                                 session->ns_data.dd_sock =
1876                                     session->ns_mover.md_sock;
1877                                 return (NDMP_NO_ERR);
1878                         }
1879 
1880                         syslog(LOG_DEBUG, "addr: %u port: %u",
1881                             mover->ndmp_mover_addr_u.addr.ip_addr,
1882                             (ulong_t)sin.sin_port);
1883 
1884                         if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
1885                                 syslog(LOG_ERR, "Socket error: %m");
1886                                 return (NDMP_IO_ERR);
1887                         }
1888                         if (connect(sock, (struct sockaddr *)&sin,
1889                             sizeof (sin)) < 0) {
1890                                 syslog(LOG_ERR, "Connect error: %m");
1891                                 (void) close(sock);
1892                                 return (NDMP_IO_ERR);
1893                         }
1894                         set_socket_options(sock);
1895                 } else {
1896                         if ((session->ns_mover.md_state !=
1897                             NDMP_MOVER_STATE_ACTIVE) ||
1898                             (session->ns_mover.md_sock == -1)) {
1899 
1900                                 syslog(LOG_DEBUG,
1901                                     "Not in active  state mover"
1902                                     "  state = %d or Illegal mover sock=%d",
1903                                     session->ns_mover.md_state,
1904                                     session->ns_mover.md_sock);
1905                                 return (NDMP_ILLEGAL_STATE_ERR);
1906                         }
1907 
1908                         sock = session->ns_mover.md_sock;
1909                         syslog(LOG_DEBUG,
1910                             "session: 0x%x setting data sock fd: %d to be"
1911                             " same as listen_sock", session, sock);
1912                 }
1913 
1914                 session->ns_data.dd_sock = sock;
1915 
1916                 return (NDMP_NO_ERR);
1917         }
1918         /* Local mover connection. */
1919 
1920         if (session->ns_mover.md_state != NDMP_MOVER_STATE_LISTEN) {
1921                 return (NDMP_ILLEGAL_STATE_ERR);
1922         }
1923         if (session->ns_tape.td_fd == -1) {
1924                 syslog(LOG_ERR, "Tape device not open");
1925                 return (NDMP_DEV_NOT_OPEN_ERR);
1926         }
1927         if (mover_mode == NDMP_MOVER_MODE_READ &&
1928             session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
1929                 syslog(LOG_ERR, "Write protected device.");
1930                 return (NDMP_WRITE_PROTECT_ERR);
1931         }
1932         session->ns_mover.md_state = NDMP_MOVER_STATE_ACTIVE;
1933         session->ns_mover.md_mode = mover_mode;
1934 
1935         return (NDMP_NO_ERR);
1936 }
1937 
1938 
1939 
1940 /*
1941  * ndmpd_mover_seek
1942  *
1943  * Seek to the requested data stream position.
1944  * If the requested offset is outside of the current window,
1945  * the mover is paused and a notify_mover_paused request is sent
1946  * notifying the client that a seek is required.
1947  * If the requested offest is within the window but not within the
1948  * current record, then the tape is positioned to the record containing
1949  * the requested offest.
1950  * The requested amount of data is then read from the tape device and
1951  * written to the data connection.
1952  *
1953  * Parameters:
1954  *   session (input) - session pointer.
1955  *   offset  (input) - data stream position to seek to.
1956  *   length  (input) - amount of data that will be read.
1957  *
1958  * Returns:
1959  *   1 - seek pending completion by the NDMP client.
1960  *   0 - seek successfully completed.
1961  *  -1 - error.
1962  */
1963 int
1964 ndmpd_mover_seek(ndmpd_session_t *session, u_longlong_t offset,
1965     u_longlong_t length)
1966 {
1967         int ctlcmd;
1968         int ctlcnt;
1969         u_longlong_t tape_position;
1970         u_longlong_t buf_position;
1971         ndmp_notify_mover_paused_request pause_request;
1972 
1973         session->ns_mover.md_seek_position = offset;
1974         session->ns_mover.md_bytes_left_to_read = length;
1975 
1976         /*
1977          * If the requested position is outside of the window,
1978          * notify the client that a seek is required.
1979          */
1980         if (session->ns_mover.md_seek_position <
1981             session->ns_mover.md_window_offset ||
1982             session->ns_mover.md_seek_position >=
1983             session->ns_mover.md_window_offset +
1984             session->ns_mover.md_window_length) {
1985 
1986                 session->ns_mover.md_w_index = 0;
1987                 session->ns_mover.md_r_index = 0;
1988 
1989                 session->ns_mover.md_state = NDMP_MOVER_STATE_PAUSED;
1990                 session->ns_mover.md_pause_reason = NDMP_MOVER_PAUSE_SEEK;
1991                 pause_request.reason = NDMP_MOVER_PAUSE_SEEK;
1992                 pause_request.seek_position = long_long_to_quad(offset);
1993 
1994                 if (ndmp_send_request(session->ns_connection,
1995                     NDMP_NOTIFY_MOVER_PAUSED, NDMP_NO_ERR,
1996                     (void *) &pause_request, 0) < 0) {
1997                         return (-1);
1998                 }
1999                 return (1);
2000         }
2001         /*
2002          * Determine the data stream position of the first byte in the
2003          * data buffer.
2004          */
2005         buf_position = session->ns_mover.md_position -
2006             (session->ns_mover.md_position % session->ns_mover.md_record_size);
2007 
2008         /*
2009          * Determine the data stream position of the next byte that
2010          * will be read from tape.
2011          */
2012         tape_position = buf_position;
2013         if (session->ns_mover.md_w_index != 0)
2014                 tape_position += session->ns_mover.md_record_size;
2015 
2016         /*
2017          * Check if requested position is for data that has been read and is
2018          * in the buffer.
2019          */
2020         if (offset >= buf_position && offset < tape_position) {
2021                 session->ns_mover.md_position = offset;
2022                 session->ns_mover.md_r_index = session->ns_mover.md_position -
2023                     buf_position;
2024                 return (0);
2025         }
2026 
2027         ctlcmd = 0;
2028         if (tape_position > session->ns_mover.md_seek_position) {
2029                 /* Need to seek backward. */
2030                 ctlcmd = MTBSR;
2031                 ctlcnt = (int)((tape_position - offset - 1)
2032                     / session->ns_mover.md_record_size) + 1;
2033                 tape_position -= ((u_longlong_t)(((tape_position - offset - 1) /
2034                     session->ns_mover.md_record_size) + 1) *
2035                     (u_longlong_t)session->ns_mover.md_record_size);
2036 
2037         } else if (offset >= tape_position + session->ns_mover.md_record_size) {
2038                 /* Need to seek forward. */
2039                 ctlcmd = MTFSR;
2040                 ctlcnt = (int)((offset - tape_position)
2041                     / session->ns_mover.md_record_size);
2042                 tape_position += ((u_longlong_t)(((offset - tape_position) /
2043                     session->ns_mover.md_record_size)) *
2044                     (u_longlong_t)session->ns_mover.md_record_size);
2045         }
2046         /* Reposition the tape if necessary. */
2047         if (ctlcmd) {
2048                 (void) ndmp_mtioctl(session->ns_tape.td_fd, ctlcmd, ctlcnt);
2049         }
2050 
2051         session->ns_mover.md_position = tape_position;
2052         session->ns_mover.md_r_index = 0;
2053         session->ns_mover.md_w_index = 0;
2054 
2055         return (0);
2056 }
2057 
2058 
2059 /* ** static functions ************************************************** */
2060 
2061 /*
2062  * create_listen_socket_v2
2063  *
2064  * Creates a socket for listening for accepting data connections.
2065  *
2066  * Parameters:
2067  *   session (input)  - session pointer.
2068  *   addr    (output) - location to store address of socket.
2069  *   port    (output) - location to store port of socket.
2070  *
2071  * Returns:
2072  *   0 - success.
2073  *  -1 - error.
2074  */
2075 static int
2076 create_listen_socket_v2(ndmpd_session_t *session, ulong_t *addr, ushort_t *port)
2077 {
2078         session->ns_mover.md_listen_sock = ndmp_create_socket(addr, port);
2079         if (session->ns_mover.md_listen_sock < 0)
2080                 return (-1);
2081 
2082         /*
2083          * Add a file handler for the listen socket.
2084          * ndmpd_select will call accept_connection when a
2085          * connection is ready to be accepted.
2086          */
2087         if (ndmpd_add_file_handler(session, (void *) session,
2088             session->ns_mover.md_listen_sock, NDMPD_SELECT_MODE_READ, HC_MOVER,
2089             accept_connection) < 0) {
2090                 (void) close(session->ns_mover.md_listen_sock);
2091                 session->ns_mover.md_listen_sock = -1;
2092                 return (-1);
2093         }
2094 
2095         return (0);
2096 }
2097 
2098 /*
2099  * accept_connection
2100  *
2101  * Accept a data connection from a data server.
2102  * Called by ndmpd_select when a connection is pending on
2103  * the mover listen socket.
2104  *
2105  * Parameters:
2106  *   cookie  (input) - session pointer.
2107  *   fd      (input) - file descriptor.
2108  *   mode    (input) - select mode.
2109  *
2110  * Returns:
2111  *   void.
2112  */
2113 /*ARGSUSED*/
2114 static void
2115 accept_connection(void *cookie, int fd, ulong_t mode)
2116 {
2117         ndmpd_session_t *session = (ndmpd_session_t *)cookie;
2118         struct sockaddr_in from;
2119         int from_len;
2120 
2121         from_len = sizeof (from);
2122         session->ns_mover.md_sock = accept(fd, (struct sockaddr *)&from,
2123             &from_len);
2124 
2125         (void) ndmpd_remove_file_handler(session, fd);
2126         (void) close(session->ns_mover.md_listen_sock);
2127         session->ns_mover.md_listen_sock = -1;
2128 
2129         if (session->ns_mover.md_sock < 0) {
2130                 syslog(LOG_ERR, "Accept error: %m");
2131                 ndmpd_mover_error(session, NDMP_MOVER_HALT_CONNECT_ERROR);
2132                 return;
2133         }
2134         set_socket_options(session->ns_mover.md_sock);
2135 
2136         if (session->ns_mover.md_mode == NDMP_MOVER_MODE_READ) {
2137                 if (start_mover_for_backup(session) < 0) {
2138                         ndmpd_mover_error(session,
2139                             NDMP_MOVER_HALT_INTERNAL_ERROR);
2140                         return;
2141                 }
2142                 syslog(LOG_DEBUG, "Backup connection established by %s:%d",
2143                     inet_ntoa(IN_ADDR(from.sin_addr.s_addr)),
2144                     ntohs(from.sin_port));
2145         } else {
2146                 syslog(LOG_DEBUG, "Restore connection established by %s:%d",
2147                     inet_ntoa(IN_ADDR(from.sin_addr.s_addr)),
2148                     ntohs(from.sin_port));
2149         }
2150 
2151         session->ns_mover.md_state = NDMP_MOVER_STATE_ACTIVE;
2152 }
2153 
2154 /*
2155  * tape_read
2156  *
2157  * Reads a data record from tape. Detects and handles EOT conditions.
2158  *
2159  * Parameters:
2160  *   session (input) - session pointer.
2161  *   data    (input) - location to read data to.
2162  *
2163  * Returns:
2164  *    0 - operation aborted.
2165  *   -1 - tape read error.
2166  *   otherwise - number of bytes read.
2167  */
2168 static int
2169 tape_read(ndmpd_session_t *session, char *data)
2170 {
2171         ssize_t n;
2172         int err;
2173         int count = session->ns_mover.md_record_size;
2174 
2175         for (; ; ) {
2176                 n = read(session->ns_tape.td_fd, data, count);
2177                 if (n < 0) {
2178                         syslog(LOG_ERR, "Tape read error: %m.");
2179                         return (TAPE_READ_ERR);
2180                 }
2181                 NS_ADD(rtape, n);
2182 
2183                 if (n == 0) {
2184                         if (!is_writer_running(session))
2185                                 return (TAPE_NO_WRITER_ERR);
2186 
2187                         /*
2188                          * End of media reached.
2189                          * Notify client and wait for the client to
2190                          * either abort the data operation or continue the
2191                          * operation after changing the tape.
2192                          */
2193                         NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
2194                             ++ndmp_log_msg_id,
2195                             "End of tape reached. Load next tape");
2196 
2197                         syslog(LOG_DEBUG,
2198                             "End of tape reached. Load next tape");
2199 
2200                         err = change_tape(session);
2201 
2202                         /* Operation aborted or connection terminated? */
2203                         if (err < 0) {
2204                                 /*
2205                                  * K.L. Go back one record if it is read
2206                                  * but not used.
2207                                  */
2208 
2209                                 if (count != session->ns_mover.md_record_size) {
2210                                         (void) ndmp_mtioctl(
2211                                             session->ns_tape.td_fd, MTBSR, 1);
2212                                 }
2213                                 return (0);
2214                         }
2215                         /* Retry the read from the new tape. */
2216                         continue;
2217                 }
2218 
2219                 /* Change to pass Veritas Netbackup prequal test. */
2220                 data += n;
2221                 count -= n;
2222                 if (count <= 0) {
2223                         session->ns_mover.md_record_num++;
2224                         session->ns_tape.td_record_count++;
2225                         return (n);
2226                 }
2227         }
2228 }
2229 
2230 /*
2231  * change_tape
2232  *
2233  * Send a notify_pause request (protocol version 1) or
2234  * notify_mover_pause request (protocol version 2) to the
2235  * NDMP client to inform
2236  * the client that a tape volume change is required.
2237  * Process messages until the data/mover operation is either aborted
2238  * or continued.
2239  *
2240  * Parameters:
2241  *   client_data (input) - session pointer.
2242  *
2243  * Returns:
2244  *   0 - operation has been continued.
2245  *  -1 - operation has been aborted.
2246  */
2247 static int
2248 change_tape(ndmpd_session_t *session)
2249 {
2250         ndmp_notify_mover_paused_request request;
2251 
2252         session->ns_mover.md_state = NDMP_MOVER_STATE_PAUSED;
2253 
2254         if (session->ns_mover.md_mode == NDMP_MOVER_MODE_READ)
2255                 session->ns_mover.md_pause_reason = NDMP_MOVER_PAUSE_EOM;
2256         else
2257                 session->ns_mover.md_pause_reason = NDMP_MOVER_PAUSE_EOF;
2258 
2259         request.reason = session->ns_mover.md_pause_reason;
2260         request.seek_position = long_long_to_quad(0LL);
2261 
2262         syslog(LOG_DEBUG, "ndmp_send_request: MOVER_PAUSED, reason: %d",
2263             session->ns_mover.md_pause_reason);
2264 
2265         if (ndmp_send_request(session->ns_connection,
2266             NDMP_NOTIFY_MOVER_PAUSED, NDMP_NO_ERR,
2267             (void *) &request, 0) < 0) {
2268                 return (-1);
2269         }
2270         /*
2271          * Wait for until the state is changed by
2272          * an abort or continue request.
2273          */
2274         return (ndmp_wait_for_mover(session));
2275 }
2276 
2277 
2278 /*
2279  * discard_data
2280  *
2281  * Read and discard data from the data connection.
2282  * Called when a module has called ndmpd_seek() prior to
2283  * reading all of the data from the previous seek.
2284  *
2285  * Parameters:
2286  *   session (input) - session pointer.
2287  *
2288  * Returns:
2289  *   number of bytes read and discarded.
2290  *  -1 - error.
2291  */
2292 static int
2293 discard_data(ndmpd_session_t *session, ulong_t length)
2294 {
2295         int n;
2296         char *addr;
2297 
2298         if ((addr = ndmp_malloc(length)) == NULL)
2299                 return (-1);
2300 
2301         /* Read and discard the data. */
2302         n = read(session->ns_mover.md_sock, addr, length);
2303         if (n < 0) {
2304                 syslog(LOG_ERR, "Socket read error: %m.");
2305                 free(addr);
2306                 return (-1);
2307         }
2308 
2309         free(addr);
2310         return (n);
2311 }
2312 
2313 
2314 /*
2315  * mover_tape_read_one_buf
2316  *
2317  * Read one buffer from the tape. This is used by mover_tape_reader
2318  *
2319  * Parameters:
2320  *   session (input) - session pointer.
2321  *   buf (input) - buffer read
2322  *
2323  * Returns:
2324  *   0: on success
2325  *  -1: otherwise
2326  */
2327 static int
2328 mover_tape_read_one_buf(ndmpd_session_t *session, tlm_buffer_t *buf)
2329 {
2330         int n;
2331 
2332         tlm_buffer_mark_empty(buf);
2333 
2334         /*
2335          * If the end of the mover window has been reached,
2336          * then notify the client that a seek is needed.
2337          * Remove the file handler to prevent this function from
2338          * being called. The handler will be reinstalled in
2339          * ndmpd_mover_continue.
2340          */
2341 
2342         if (session->ns_mover.md_position >=
2343             session->ns_mover.md_window_offset +
2344             session->ns_mover.md_window_length) {
2345                 ndmp_notify_mover_paused_request pause_request;
2346 
2347                 session->ns_mover.md_state = NDMP_MOVER_STATE_PAUSED;
2348                 session->ns_mover.md_pause_reason = NDMP_MOVER_PAUSE_SEEK;
2349                 pause_request.reason = NDMP_MOVER_PAUSE_SEEK;
2350                 pause_request.seek_position =
2351                     long_long_to_quad(session->ns_mover.md_position);
2352 
2353                 if (ndmp_send_request(session->ns_connection,
2354                     NDMP_NOTIFY_MOVER_PAUSED, NDMP_NO_ERR,
2355                     (void *) &pause_request, 0) < 0) {
2356                         ndmpd_mover_error(session,
2357                             NDMP_MOVER_HALT_INTERNAL_ERROR);
2358                 }
2359                 buf->tb_errno = EIO;
2360                 return (TAPE_READ_ERR);
2361         }
2362 
2363         n = tape_read(session, buf->tb_buffer_data);
2364 
2365         if (n <= 0) {
2366                 if (n < 0)
2367                         ndmpd_mover_error(session,
2368                             (n == 0 ? NDMP_MOVER_HALT_ABORTED :
2369                             NDMP_MOVER_HALT_INTERNAL_ERROR));
2370                 return (TAPE_READ_ERR);
2371         }
2372 
2373         buf->tb_full = TRUE;
2374         buf->tb_buffer_size = session->ns_mover.md_record_size;
2375 
2376         /*
2377          * Discard data if the current data stream position is
2378          * prior to the seek position. This is necessary if a seek
2379          * request set the seek pointer to a position that is not a
2380          * record boundary. The seek request handler can only position
2381          * to the start of a record.
2382          */
2383         if (session->ns_mover.md_position < session->ns_mover.md_seek_position)
2384                 session->ns_mover.md_position =
2385                     session->ns_mover.md_seek_position;
2386 
2387         return (0);
2388 }
2389 
2390 
2391 /*
2392  * mover_tape_reader
2393  *
2394  * Mover tape reader thread. It is launched when the mover is started
2395  * for restore.
2396  *
2397  * Parameters:
2398  *   session (input) - session pointer.
2399  *
2400  * Returns:
2401  *   0: on success
2402  *  -1: otherwise
2403  */
2404 int
2405 mover_tape_reader(ndmpd_session_t *session)
2406 {
2407         int bidx;       /* buffer index */
2408         int rv;
2409         ndmp_lbr_params_t *nlp;
2410         tlm_buffer_t *buf;
2411         tlm_buffers_t *bufs;
2412         tlm_cmd_t *lcmd;        /* Local command */
2413         tlm_commands_t *cmds;   /* Commands structure */
2414 
2415         if ((nlp = ndmp_get_nlp(session)) == NULL) {
2416                 return (-1);
2417         }
2418 
2419         cmds = &nlp->nlp_cmds;
2420         lcmd = cmds->tcs_command;
2421         bufs = lcmd->tc_buffers;
2422 
2423         lcmd->tc_ref++;
2424         cmds->tcs_reader_count++;
2425 
2426         /*
2427          * Let our parent thread know that we are running.
2428          */
2429         tlm_cmd_signal(cmds->tcs_command, TLM_TAPE_READER);
2430 
2431         buf = tlm_buffer_in_buf(bufs, &bidx);
2432         while (cmds->tcs_reader == TLM_RESTORE_RUN &&
2433             lcmd->tc_reader == TLM_RESTORE_RUN) {
2434                 buf = tlm_buffer_in_buf(bufs, NULL);
2435 
2436                 if (buf->tb_full) {
2437                         syslog(LOG_DEBUG, "R%d", bidx);
2438                         /*
2439                          * The buffer is still full, wait for the consumer
2440                          * thread to use it.
2441                          */
2442                         tlm_buffer_out_buf_timed_wait(bufs, 100);
2443 
2444                 } else {
2445                         syslog(LOG_DEBUG, "r%d", bidx);
2446 
2447                         rv = mover_tape_read_one_buf(session, buf);
2448                         /*
2449                          * If there was an error while reading, such as
2450                          * end of stream.
2451                          */
2452                         if (rv < 0) {
2453                                 syslog(LOG_DEBUG, "Exiting, rv: %d", rv);
2454                                 break;
2455                         }
2456 
2457                         /*
2458                          * Can we do more buffering?
2459                          */
2460                         if (is_buffer_erroneous(buf)) {
2461                                 syslog(LOG_DEBUG,
2462                                     "Exiting, errno: %d, eot: %d, eof: %d",
2463                                     buf->tb_errno, buf->tb_eot, buf->tb_eof);
2464                                 break;
2465                         }
2466 
2467                         (void) tlm_buffer_advance_in_idx(bufs);
2468                         tlm_buffer_release_in_buf(bufs);
2469                         bidx = bufs->tbs_buffer_in;
2470                 }
2471         }
2472 
2473         /* If the consumer is waiting for us, wake it up. */
2474         tlm_buffer_release_in_buf(bufs);
2475 
2476         /*
2477          * Clean up.
2478          */
2479         cmds->tcs_reader_count--;
2480         lcmd->tc_ref--;
2481         lcmd->tc_writer = TLM_STOP;
2482         return (0);
2483 }
2484 
2485 
2486 /*
2487  * mover_socket_write_one_buf
2488  *
2489  * Write one buffer to the network socket. This is used by mover_socket_writer
2490  *
2491  * Parameters:
2492  *   session (input) - session pointer.
2493  *   buf (input) - buffer read
2494  *
2495  * Returns:
2496  *   0: on success
2497  *  -1: otherwise
2498  */
2499 static int
2500 mover_socket_write_one_buf(ndmpd_session_t *session, tlm_buffer_t *buf)
2501 {
2502         int n;
2503 
2504         /* Write the data to the data connection. */
2505         errno = 0;
2506         n = write(session->ns_mover.md_sock, buf->tb_buffer_data,
2507             buf->tb_buffer_size);
2508 
2509         if (n < 0) {
2510                 ndmpd_mover_error(session, NDMP_MOVER_HALT_CONNECT_CLOSED);
2511                 return (-1);
2512         }
2513 
2514         session->ns_mover.md_position += n;
2515         session->ns_mover.md_bytes_left_to_read -= n;
2516         tlm_buffer_mark_empty(buf);
2517 
2518         /*
2519          * If the read limit has been reached,
2520          * then remove the file handler to prevent this
2521          * function from getting called. The next mover_read request
2522          * will reinstall the handler.
2523          */
2524         if (session->ns_mover.md_bytes_left_to_read == 0) {
2525                 (void) ndmpd_remove_file_handler(session,
2526                     session->ns_mover.md_sock);
2527                 return (-1);
2528         }
2529 
2530         return (0);
2531 }
2532 
2533 
2534 
2535 /*
2536  * mover_socket_writer
2537  *
2538  * Mover's socket writer thread. This thread sends the read buffer
2539  * from the tape to the data server through the network socket.
2540  *
2541  * Parameters:
2542  *   session (input) - session pointer.
2543  *
2544  * Returns:
2545  *   0: on success
2546  *  -1: otherwise
2547  */
2548 int
2549 mover_socket_writer(ndmpd_session_t *session)
2550 {
2551         int bidx;       /* buffer index */
2552         ndmp_lbr_params_t *nlp;
2553         tlm_buffer_t *buf;
2554         tlm_buffers_t *bufs;
2555         tlm_cmd_t *lcmd;        /* Local command */
2556         tlm_commands_t *cmds;   /* Commands structure */
2557 
2558         if ((nlp = ndmp_get_nlp(session)) == NULL) {
2559                 return (-1);
2560         }
2561 
2562         cmds = &nlp->nlp_cmds;
2563         lcmd = cmds->tcs_command;
2564         bufs = lcmd->tc_buffers;
2565 
2566         lcmd->tc_ref++;
2567         cmds->tcs_writer_count++;
2568 
2569         /*
2570          * Let our parent thread know that we are running.
2571          */
2572         tlm_cmd_signal(cmds->tcs_command, TLM_SOCK_WRITER);
2573 
2574         bidx = bufs->tbs_buffer_out;
2575         while (cmds->tcs_writer != (int)TLM_ABORT &&
2576             lcmd->tc_writer != (int)TLM_ABORT) {
2577                 buf = &bufs->tbs_buffer[bidx];
2578 
2579                 if (buf->tb_full) {
2580                         syslog(LOG_DEBUG, "w%d", bidx);
2581 
2582                         if (mover_socket_write_one_buf(session, buf) < 0) {
2583                                 break;
2584                         }
2585 
2586                         (void) tlm_buffer_advance_out_idx(bufs);
2587                         tlm_buffer_release_out_buf(bufs);
2588                         bidx = bufs->tbs_buffer_out;
2589                 } else {
2590                         if (lcmd->tc_writer != TLM_RESTORE_RUN) {
2591                                 /* No more data is coming, time to exit */
2592                                 break;
2593                         }
2594                         syslog(LOG_DEBUG, "W%d", bidx);
2595                         /*
2596                          * The buffer is not full, wait for the producer
2597                          * thread to fill it.
2598                          */
2599                         tlm_buffer_in_buf_timed_wait(bufs, 100);
2600                 }
2601         }
2602 
2603         if (cmds->tcs_writer == (int)TLM_ABORT)
2604                 syslog(LOG_DEBUG, "cmds->tcs_writer == (int)TLM_ABORT");
2605         if (lcmd->tc_writer == (int)TLM_ABORT)
2606                 syslog(LOG_DEBUG, "lcmd->tc_writer == TLM_ABORT");
2607 
2608         /* If the producer is waiting for us, wake it up. */
2609         tlm_buffer_release_out_buf(bufs);
2610 
2611         /*
2612          * Clean up.
2613          */
2614         cmds->tcs_writer_count--;
2615         lcmd->tc_ref--;
2616         lcmd->tc_reader = TLM_STOP;
2617         return (0);
2618 }
2619 
2620 
2621 /*
2622  * start_mover_for_restore
2623  *
2624  * Creates the mover tape reader and network writer threads for
2625  * the mover to perform the 3-way restore.
2626  *
2627  * Parameters:
2628  *   session (input) - session pointer.
2629  *
2630  * Returns:
2631  *   0: on success
2632  *  -1: otherwise
2633  */
2634 static int
2635 start_mover_for_restore(ndmpd_session_t *session)
2636 {
2637         ndmp_lbr_params_t *nlp;
2638         tlm_commands_t *cmds;
2639         long xfer_size;
2640         int rc;
2641 
2642         if ((nlp = ndmp_get_nlp(session)) == NULL) {
2643                 return (-1);
2644         }
2645 
2646         cmds = &nlp->nlp_cmds;
2647         (void) memset(cmds, 0, sizeof (*cmds));
2648         cmds->tcs_reader = cmds->tcs_writer = TLM_RESTORE_RUN;
2649         xfer_size = ndmp_buffer_get_size(session);
2650         cmds->tcs_command = tlm_create_reader_writer_ipc(FALSE, xfer_size);
2651         if (cmds->tcs_command == NULL)
2652                 return (-1);
2653 
2654         cmds->tcs_command->tc_reader = TLM_RESTORE_RUN;
2655         cmds->tcs_command->tc_writer = TLM_RESTORE_RUN;
2656 
2657         /*
2658          * We intentionnally don't wait for the threads to start since the
2659          * reply of the request (which resulted in calling this function)
2660          * must be sent to the client before probable errors are sent
2661          * to the client.
2662          */
2663         rc = pthread_create(NULL, NULL, (funct_t)mover_tape_reader, session);
2664         if (rc == 0) {
2665                 tlm_cmd_wait(cmds->tcs_command, TLM_TAPE_READER);
2666         } else {
2667                 return (-1);
2668         }
2669 
2670         rc = pthread_create(NULL, NULL, (funct_t)mover_socket_writer, session);
2671         if (rc == 0) {
2672                 tlm_cmd_wait(cmds->tcs_command, TLM_SOCK_WRITER);
2673         } else {
2674                 return (-1);
2675         }
2676 
2677         tlm_release_reader_writer_ipc(cmds->tcs_command);
2678         return (0);
2679 }
2680 
2681 
2682 /*
2683  * mover_socket_read_one_buf
2684  *
2685  * Read one buffer from the network socket for the mover. This is used
2686  * by mover_socket_reader
2687  *
2688  * Parameters:
2689  *   session (input) - session pointer.
2690  *   buf (input) - buffer read
2691  *   read_size (input) - size to be read
2692  *
2693  * Returns:
2694  *   0: on success
2695  *  -1: otherwise
2696  */
2697 static int
2698 mover_socket_read_one_buf(ndmpd_session_t *session, tlm_buffer_t *buf,
2699     long read_size)
2700 {
2701         int n, index;
2702         long toread;
2703 
2704         tlm_buffer_mark_empty(buf);
2705         for (index = 0, toread = read_size; toread > 0; ) {
2706                 errno = 0;
2707                 n = read(session->ns_mover.md_sock, &buf->tb_buffer_data[index],
2708                     toread);
2709                 if (n == 0) {
2710                         break;
2711                 } else if (n > 0) {
2712                         index += n;
2713                         toread -= n;
2714                 } else {
2715                         buf->tb_eof = TRUE;
2716                         buf->tb_errno = errno;
2717                         buf->tb_buffer_size = 0;
2718                         return (-1);
2719                 }
2720         }
2721 
2722         if (index > 0) {
2723                 buf->tb_full = TRUE;
2724                 buf->tb_buffer_size = read_size;
2725                 if (read_size > 0)
2726                         (void) memset(&buf->tb_buffer_data[index], 0,
2727                             read_size - index);
2728         } else {
2729                 buf->tb_eof = TRUE;
2730                 buf->tb_buffer_size = 0;
2731         }
2732 
2733         syslog(LOG_DEBUG, "full: %d, eot: %d, eof: %d,"
2734             " errno: %d, size: %d, data: 0x%x",
2735             buf->tb_full, buf->tb_eot, buf->tb_eof, buf->tb_errno,
2736             buf->tb_buffer_size, buf->tb_buffer_data);
2737 
2738         return (0);
2739 }
2740 
2741 
2742 
2743 /*
2744  * mover_socket_reader
2745  *
2746  * Mover socket reader thread. This is used when reading data from the
2747  * network socket for performing remote backups.
2748  *
2749  * Parameters:
2750  *   session (input) - session pointer.
2751  *
2752  * Returns:
2753  *   0: on success
2754  *  -1: otherwise
2755  */
2756 int
2757 mover_socket_reader(ndmpd_session_t *session)
2758 {
2759         int bidx;       /* buffer index */
2760         ndmp_lbr_params_t *nlp;
2761         tlm_buffer_t *buf;
2762         tlm_buffers_t *bufs;
2763         tlm_cmd_t *lcmd;        /* Local command */
2764         tlm_commands_t *cmds;   /* Commands structure */
2765         static int nr = 0;
2766 
2767         if ((nlp = ndmp_get_nlp(session)) == NULL) {
2768                 return (-1);
2769         }
2770 
2771         cmds = &nlp->nlp_cmds;
2772         lcmd = cmds->tcs_command;
2773         bufs = lcmd->tc_buffers;
2774 
2775         lcmd->tc_ref++;
2776         cmds->tcs_reader_count++;
2777 
2778         /*
2779          * Let our parent thread know that we are running.
2780          */
2781         tlm_cmd_signal(cmds->tcs_command, TLM_SOCK_READER);
2782 
2783         bidx = bufs->tbs_buffer_in;
2784         while (cmds->tcs_reader == TLM_BACKUP_RUN &&
2785             lcmd->tc_reader == TLM_BACKUP_RUN) {
2786                 buf = &bufs->tbs_buffer[bidx];
2787 
2788                 if (buf->tb_full) {
2789                         syslog(LOG_DEBUG, "R%d", bidx);
2790                         /*
2791                          * The buffer is still full, wait for the consumer
2792                          * thread to use it.
2793                          */
2794                         tlm_buffer_out_buf_timed_wait(bufs, 100);
2795                 } else {
2796                         syslog(LOG_DEBUG, "r%d, nr: %d", bidx, ++nr);
2797 
2798                         (void) mover_socket_read_one_buf(session, buf,
2799                             bufs->tbs_data_transfer_size);
2800 
2801                         /*
2802                          * Can we do more buffering?
2803                          */
2804                         if (is_buffer_erroneous(buf)) {
2805                                 syslog(LOG_DEBUG,
2806                                     "Exiting, errno: %d, eot: %d, eof: %d",
2807                                     buf->tb_errno, buf->tb_eot, buf->tb_eof);
2808                                 break;
2809                         }
2810 
2811                         (void) tlm_buffer_advance_in_idx(bufs);
2812                         tlm_buffer_release_in_buf(bufs);
2813                         bidx = bufs->tbs_buffer_in;
2814                 }
2815         }
2816 
2817         if (cmds->tcs_reader != TLM_BACKUP_RUN)
2818                 syslog(LOG_DEBUG, "cmds->tcs_reader != TLM_BACKUP_RUN");
2819         if (lcmd->tc_reader != TLM_BACKUP_RUN)
2820                 syslog(LOG_DEBUG, "lcmd->tc_reader != TLM_BACKUP_RUN");
2821         syslog(LOG_DEBUG, "nr: %d", nr);
2822 
2823         /* If the consumer is waiting for us, wake it up. */
2824         tlm_buffer_release_in_buf(bufs);
2825 
2826         /*
2827          * Clean up.
2828          */
2829         cmds->tcs_reader_count--;
2830         lcmd->tc_ref--;
2831         lcmd->tc_writer = TLM_STOP;
2832         return (0);
2833 }
2834 
2835 
2836 /*
2837  * mover_tape_writer_one_buf
2838  *
2839  * Write one buffer for the mover to the local tape device. This is
2840  * used by mover_tape_writer thread.
2841  *
2842  * Parameters:
2843  *   session (input) - session pointer.
2844  *   buf (input) - buffer read
2845  *
2846  * Returns:
2847  *   0: on success
2848  *  -1: otherwise
2849  */
2850 static int
2851 mover_tape_write_one_buf(ndmpd_session_t *session, tlm_buffer_t *buf)
2852 {
2853         int n;
2854 
2855         syslog(LOG_DEBUG, "full: %d, eot: %d, eof: %d,"
2856             " errno: %d, size: %d, data: 0x%x",
2857             buf->tb_full, buf->tb_eot, buf->tb_eof, buf->tb_errno,
2858             buf->tb_buffer_size, buf->tb_buffer_data);
2859 
2860         n = mover_tape_write_v3(session, buf->tb_buffer_data,
2861             buf->tb_buffer_size);
2862 
2863         if (n <= 0) {
2864                 ndmpd_mover_error(session, (n == 0 ? NDMP_MOVER_HALT_ABORTED
2865                     : NDMP_MOVER_HALT_INTERNAL_ERROR));
2866                 return (-1);
2867         }
2868         session->ns_mover.md_position += n;
2869         session->ns_mover.md_data_written += n;
2870         session->ns_mover.md_record_num++;
2871 
2872         tlm_buffer_mark_empty(buf);
2873 
2874         return (0);
2875 }
2876 
2877 
2878 /*
2879  * mover_tape_writer
2880  *
2881  * Mover tape writer thread. This is used for performing remote backups
2882  * in a 3-way configuration. It writes the data from network socket to
2883  * the locally attached tape device.
2884  *
2885  * Parameters:
2886  *   session (input) - session pointer.
2887  *
2888  * Returns:
2889  *   0: on success
2890  *  -1: otherwise
2891  */
2892 int
2893 mover_tape_writer(ndmpd_session_t *session)
2894 {
2895         int bidx;
2896         ndmp_lbr_params_t *nlp;
2897         tlm_buffer_t *buf;
2898         tlm_buffers_t *bufs;
2899         tlm_cmd_t *lcmd;
2900         tlm_commands_t *cmds;
2901         static int nw = 0;
2902 
2903         if ((nlp = ndmp_get_nlp(session)) == NULL) {
2904                 return (-1);
2905         }
2906 
2907         cmds = &nlp->nlp_cmds;
2908         lcmd = cmds->tcs_command;
2909         bufs = lcmd->tc_buffers;
2910 
2911         lcmd->tc_ref++;
2912         cmds->tcs_writer_count++;
2913 
2914         /*
2915          * Let our parent thread know that we are running.
2916          */
2917         tlm_cmd_signal(cmds->tcs_command, TLM_TAPE_WRITER);
2918 
2919         bidx = bufs->tbs_buffer_out;
2920         buf = &bufs->tbs_buffer[bidx];
2921         while (cmds->tcs_writer != (int)TLM_ABORT &&
2922             lcmd->tc_writer != (int)TLM_ABORT) {
2923                 if (buf->tb_full) {
2924                         syslog(LOG_DEBUG, "w%d, nw: %d", bidx, ++nw);
2925 
2926                         if (mover_tape_write_one_buf(session, buf) < 0) {
2927                                 syslog(LOG_DEBUG,
2928                                     "mover_tape_write_one_buf() failed");
2929                                 break;
2930                         }
2931 
2932                         (void) tlm_buffer_advance_out_idx(bufs);
2933                         tlm_buffer_release_out_buf(bufs);
2934                         bidx = bufs->tbs_buffer_out;
2935                         buf = &bufs->tbs_buffer[bidx];
2936                 } else {
2937                         if (lcmd->tc_writer != TLM_BACKUP_RUN) {
2938                                 /* No more data is coming, time to exit */
2939                                 break;
2940                         }
2941                         syslog(LOG_DEBUG, "W%d", bidx);
2942                         /*
2943                          * The buffer is not full, wait for the producer
2944                          * thread to fill it.
2945                          */
2946                         tlm_buffer_in_buf_timed_wait(bufs, 100);
2947                 }
2948         }
2949 
2950         if (cmds->tcs_writer == (int)TLM_ABORT)
2951                 syslog(LOG_DEBUG, "cmds->tcs_writer == TLM_ABORT");
2952         if (lcmd->tc_writer == (int)TLM_ABORT)
2953                 syslog(LOG_DEBUG, "lcmd->tc_writer == TLM_ABORT");
2954 
2955         if (buf->tb_errno == 0) {
2956                 ndmpd_mover_error(session, NDMP_MOVER_HALT_CONNECT_CLOSED);
2957         } else {
2958                 ndmpd_mover_error(session, NDMP_MOVER_HALT_INTERNAL_ERROR);
2959         }
2960 
2961         /* If the producer is waiting for us, wake it up. */
2962         tlm_buffer_release_out_buf(bufs);
2963 
2964         /*
2965          * Clean up.
2966          */
2967         cmds->tcs_writer_count--;
2968         lcmd->tc_ref--;
2969         lcmd->tc_reader = TLM_STOP;
2970         return (0);
2971 }
2972 
2973 
2974 /*
2975  * start_mover_for_backup
2976  *
2977  * Starts a remote backup by running socket reader and tape
2978  * writer threads. The mover runs a remote backup in a 3-way backup
2979  * configuration.
2980  *
2981  * Parameters:
2982  *   session (input) - session pointer.
2983  *
2984  * Returns:
2985  *   0: on success
2986  *  -1: otherwise
2987  */
2988 static int
2989 start_mover_for_backup(ndmpd_session_t *session)
2990 {
2991         ndmp_lbr_params_t *nlp;
2992         tlm_commands_t *cmds;
2993         int rc;
2994 
2995         if ((nlp = ndmp_get_nlp(session)) == NULL) {
2996                 return (-1);
2997         }
2998 
2999         cmds = &nlp->nlp_cmds;
3000         (void) memset(cmds, 0, sizeof (*cmds));
3001         cmds->tcs_reader = cmds->tcs_writer = TLM_BACKUP_RUN;
3002         cmds->tcs_command = tlm_create_reader_writer_ipc(TRUE,
3003             session->ns_mover.md_record_size);
3004         if (cmds->tcs_command == NULL)
3005                 return (-1);
3006 
3007         cmds->tcs_command->tc_reader = TLM_BACKUP_RUN;
3008         cmds->tcs_command->tc_writer = TLM_BACKUP_RUN;
3009 
3010         /*
3011          * We intentionally don't wait for the threads to start since the
3012          * reply of the request (which resulted in calling this function)
3013          * must be sent to the client before probable errors are sent
3014          * to the client.
3015          */
3016         rc = pthread_create(NULL, NULL, (funct_t)mover_socket_reader, session);
3017         if (rc == 0) {
3018                 tlm_cmd_wait(cmds->tcs_command, TLM_SOCK_READER);
3019         } else {
3020                 return (-1);
3021         }
3022 
3023         rc = pthread_create(NULL, NULL, (funct_t)mover_tape_writer, session);
3024         if (rc == 0) {
3025                 tlm_cmd_wait(cmds->tcs_command, TLM_TAPE_WRITER);
3026         } else {
3027                 return (-1);
3028         }
3029 
3030         tlm_release_reader_writer_ipc(cmds->tcs_command);
3031         return (0);
3032 }
3033 
3034 
3035 /*
3036  * is_writer_running
3037  *
3038  * Find out if the writer thread has started or not.
3039  *
3040  * Parameters:
3041  *   session (input) - session pointer.
3042  *
3043  * Returns:
3044  *   0: not started
3045  *   non-zero: started
3046  *      Note: non-zero is also returned if the backup type is
3047  *              neither TAR nor DUMP.  I.e. the is_writer_running()
3048  *              check does not apply in this case and things should
3049  *              appear successful.
3050  */
3051 static boolean_t
3052 is_writer_running(ndmpd_session_t *session)
3053 {
3054         boolean_t rv;
3055         ndmp_lbr_params_t *nlp;
3056 
3057         if (session && (session->ns_butype > NDMP_BUTYPE_DUMP))
3058                 return (1);
3059 
3060         if (session == NULL)
3061                 rv = 0;
3062         else if ((nlp = ndmp_get_nlp(session)) == NULL)
3063                 rv = 0;
3064         else
3065                 rv = (nlp->nlp_cmds.tcs_writer_count > 0);
3066 
3067         return (rv);
3068 }
3069 
3070 
3071 /*
3072  * is_writer_running_v3
3073  *
3074  * Find out if the writer thread has started or not.
3075  *
3076  * Parameters:
3077  *   session (input) - session pointer.
3078  *
3079  * Returns:
3080  *   0: not started
3081  *   non-zero: started
3082  *      Note: non-zero is also returned if the backup type is
3083  *              neither TAR nor DUMP.  I.e. the is_writer_running()
3084  *              check does not apply in this case and things should
3085  *              appear successful.
3086  */
3087 static boolean_t
3088 is_writer_running_v3(ndmpd_session_t *session)
3089 {
3090         boolean_t rv;
3091         ndmp_lbr_params_t *nlp;
3092 
3093         if (session && (session->ns_butype > NDMP_BUTYPE_DUMP))
3094                 return (1);
3095 
3096         if (session == NULL)
3097                 rv = 0;
3098         else if (session->ns_mover.md_data_addr.addr_type == NDMP_ADDR_TCP)
3099                 rv = 1;
3100         else if ((nlp = ndmp_get_nlp(session)) == NULL)
3101                 rv = 0;
3102         else
3103                 rv = (nlp->nlp_cmds.tcs_writer_count > 0);
3104 
3105         return (rv);
3106 }
3107 
3108 
3109 /*
3110  * ndmpd_mover_error_send
3111  *
3112  * This function sends the notify message to the client.
3113  *
3114  * Parameters:
3115  *   session (input) - session pointer.
3116  *   reason  (input) - halt reason.
3117  *
3118  * Returns:
3119  *   Error code
3120  */
3121 int
3122 ndmpd_mover_error_send(ndmpd_session_t *session, ndmp_mover_halt_reason reason)
3123 {
3124         ndmp_notify_mover_halted_request req;
3125 
3126         req.reason = reason;
3127         req.text_reason = "";
3128 
3129         return (ndmp_send_request(session->ns_connection,
3130             NDMP_NOTIFY_MOVER_HALTED, NDMP_NO_ERR, (void *)&req, 0));
3131 }
3132 
3133 
3134 /*
3135  * ndmpd_mover_error_send_v4
3136  *
3137  * This function sends the notify message to the client.
3138  *
3139  * Parameters:
3140  *   session (input) - session pointer.
3141  *   reason  (input) - halt reason.
3142  *
3143  * Returns:
3144  *   Error code
3145  */
3146 int
3147 ndmpd_mover_error_send_v4(ndmpd_session_t *session,
3148     ndmp_mover_halt_reason reason)
3149 {
3150         ndmp_notify_mover_halted_request_v4 req;
3151 
3152         req.reason = reason;
3153 
3154         return (ndmp_send_request(session->ns_connection,
3155             NDMP_NOTIFY_MOVER_HALTED, NDMP_NO_ERR, (void *)&req, 0));
3156 }
3157 
3158 
3159 /*
3160  * ndmpd_mover_error
3161  *
3162  * This function is called when an unrecoverable mover error
3163  * has been detected. A notify message is sent to the client and the
3164  * mover is placed into the halted state.
3165  *
3166  * Parameters:
3167  *   session (input) - session pointer.
3168  *   reason  (input) - halt reason.
3169  *
3170  * Returns:
3171  *   void.
3172  */
3173 void
3174 ndmpd_mover_error(ndmpd_session_t *session, ndmp_mover_halt_reason reason)
3175 {
3176         ndmp_lbr_params_t *nlp = ndmp_get_nlp(session);
3177 
3178         if (session->ns_mover.md_state == NDMP_MOVER_STATE_HALTED ||
3179             (session->ns_protocol_version > NDMPV2 &&
3180             session->ns_mover.md_state == NDMP_MOVER_STATE_IDLE))
3181                 return;
3182 
3183         if (session->ns_protocol_version == NDMPV4) {
3184                 if (ndmpd_mover_error_send_v4(session, reason) < 0)
3185                         syslog(LOG_ERR,
3186                             "Error sending notify_mover_halted request");
3187         } else {
3188                 /* No media error in V3 */
3189                 if (reason == NDMP_MOVER_HALT_MEDIA_ERROR)
3190                         reason = NDMP_MOVER_HALT_INTERNAL_ERROR;
3191                 if (ndmpd_mover_error_send(session, reason) < 0)
3192                         syslog(LOG_ERR,
3193                             "Error sending notify_mover_halted request");
3194         }
3195 
3196         (void) mutex_lock(&nlp->nlp_mtx);
3197         if (session->ns_mover.md_listen_sock != -1) {
3198                 (void) ndmpd_remove_file_handler(session,
3199                     session->ns_mover.md_listen_sock);
3200                 (void) close(session->ns_mover.md_listen_sock);
3201                 session->ns_mover.md_listen_sock = -1;
3202         }
3203         if (session->ns_mover.md_sock != -1) {
3204                 (void) ndmpd_remove_file_handler(session,
3205                     session->ns_mover.md_sock);
3206                 (void) close(session->ns_mover.md_sock);
3207                 session->ns_mover.md_sock = -1;
3208         }
3209 
3210         session->ns_mover.md_state = NDMP_MOVER_STATE_HALTED;
3211         session->ns_mover.md_halt_reason = reason;
3212         (void) cond_broadcast(&nlp->nlp_cv);
3213         (void) mutex_unlock(&nlp->nlp_mtx);
3214 }
3215 
3216 
3217 /*
3218  * mover_pause_v3
3219  *
3220  * Send an ndmp_notify_mover_paused request to the
3221  * NDMP client to inform the client that its attention is required.
3222  * Process messages until the data/mover operation is either aborted
3223  * or continued.
3224  *
3225  * Parameters:
3226  *   client_data (input) - session pointer.
3227  *   reason (input) - pause reason.
3228  *
3229  * Returns:
3230  *   0 - operation has been continued.
3231  *  -1 - operation has been aborted.
3232  */
3233 static int
3234 mover_pause_v3(ndmpd_session_t *session, ndmp_mover_pause_reason reason)
3235 {
3236         int rv;
3237         ndmp_notify_mover_paused_request request;
3238 
3239         rv = 0;
3240         session->ns_mover.md_state = NDMP_MOVER_STATE_PAUSED;
3241         session->ns_mover.md_pause_reason = reason;
3242         session->ns_mover.md_pre_cond = FALSE;
3243 
3244         request.reason = session->ns_mover.md_pause_reason;
3245         request.seek_position =
3246             long_long_to_quad(session->ns_mover.md_position);
3247 
3248         if (ndmp_send_request(session->ns_connection, NDMP_NOTIFY_MOVER_PAUSED,
3249             NDMP_NO_ERR, (void *)&request, 0) < 0) {
3250                 syslog(LOG_ERR,
3251                     "Error sending notify_mover_paused_request");
3252                 return (-1);
3253         }
3254 
3255         /*
3256          * 3-way operations are single-thread.  The same thread
3257          * should process the messages.
3258          *
3259          * 2-way operations are multi-thread.  The main thread
3260          * processes the messages.  We just need to wait and
3261          * see if the mover state changes or the operation aborts.
3262          */
3263         if (session->ns_mover.md_data_addr.addr_type == NDMP_ADDR_TCP) {
3264                 /*
3265                  * Process messages until the state is changed by
3266                  * an abort, continue, or close request .
3267                  */
3268                 for (; ; ) {
3269                         if (ndmpd_select(session, TRUE, HC_CLIENT) < 0)
3270                                 return (-1);
3271 
3272                         if (session->ns_eof == TRUE)
3273                                 return (-1);
3274 
3275                         switch (session->ns_mover.md_state) {
3276                         case NDMP_MOVER_STATE_ACTIVE:
3277                                 session->ns_tape.td_record_count = 0;
3278                                 return (0);
3279 
3280                         case NDMP_MOVER_STATE_PAUSED:
3281                                 continue;
3282 
3283                         default:
3284                                 return (-1);
3285                         }
3286                 }
3287 
3288         } else {
3289                 if (session->ns_mover.md_data_addr.addr_type ==
3290                     NDMP_ADDR_LOCAL) {
3291                         rv = ndmp_wait_for_mover(session);
3292                 } else {
3293                         rv = -1;
3294                 }
3295         }
3296 
3297         return (rv);
3298 }
3299 
3300 
3301 /*
3302  * mover_tape_write_v3
3303  *
3304  * Writes a data record to tape. Detects and handles EOT conditions.
3305  *
3306  * Parameters:
3307  *   session (input) - session pointer.
3308  *   data    (input) - data to be written.
3309  *   length  (input) - length of data to be written.
3310  *
3311  * Returns:
3312  *    0 - operation aborted by client.
3313  *   -1 - error.
3314  *   otherwise - number of bytes written.
3315  */
3316 static int
3317 mover_tape_write_v3(ndmpd_session_t *session, char *data, ssize_t length)
3318 {
3319         ssize_t n;
3320         ssize_t count = length;
3321 
3322         while (count > 0) {
3323                 /*
3324                  * Enforce mover window on write.
3325                  */
3326                 if (session->ns_mover.md_position >=
3327                     session->ns_mover.md_window_offset +
3328                     session->ns_mover.md_window_length) {
3329                         syslog(LOG_DEBUG, "MOVER_PAUSE_EOW");
3330 
3331                         if (mover_pause_v3(session, NDMP_MOVER_PAUSE_EOW) < 0)
3332                                 /* Operation aborted or connection terminated */
3333                                 return (-1);
3334 
3335                 }
3336 
3337                 n = write(session->ns_tape.td_fd, data, count);
3338                 if (n < 0) {
3339                         syslog(LOG_ERR, "Tape write error: %m.");
3340                         return (-1);
3341                 } else if (n > 0) {
3342                         NS_ADD(wtape, n);
3343                         count -= n;
3344                         data += n;
3345                         session->ns_tape.td_record_count++;
3346                 }
3347 
3348                 /* EOM handling */
3349                 if (count > 0) {
3350                         struct mtget mtstatus;
3351 
3352                         (void) ioctl(session->ns_tape.td_fd, MTIOCGET,
3353                             &mtstatus);
3354                         syslog(LOG_DEBUG, "EOM detected (%d written bytes, "
3355                             "mover record %d, file #%d, block #%d)", n,
3356                             session->ns_tape.td_record_count,
3357                             mtstatus.mt_fileno, mtstatus.mt_blkno);
3358 
3359                         /*
3360                          * Notify the client to either abort the operation
3361                          * or change the tape.
3362                          */
3363                         NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
3364                             ++ndmp_log_msg_id,
3365                             "End of tape reached. Load next tape");
3366 
3367                         if (mover_pause_v3(session, NDMP_MOVER_PAUSE_EOM) < 0)
3368                                 /* Operation aborted or connection terminated */
3369                                 return (-1);
3370                 }
3371         }
3372 
3373         return (length);
3374 }
3375 
3376 
3377 /*
3378  * mover_tape_flush_v3
3379  *
3380  * Writes all remaining buffered data to tape. A partial record is
3381  * padded out to a full record with zeros.
3382  *
3383  * Parameters:
3384  *   session (input) - session pointer.
3385  *   data    (input) - data to be written.
3386  *   length  (input) - length of data to be written.
3387  *
3388  * Returns:
3389  *   -1 - error.
3390  *   otherwise - number of bytes written.
3391  */
3392 static int
3393 mover_tape_flush_v3(ndmpd_session_t *session)
3394 {
3395         int n;
3396 
3397         if (session->ns_mover.md_w_index == 0)
3398                 return (0);
3399 
3400         (void) memset((void*)&session->ns_mover.md_buf[session->
3401             ns_mover.md_w_index], 0,
3402             session->ns_mover.md_record_size - session->ns_mover.md_w_index);
3403 
3404         n = mover_tape_write_v3(session, session->ns_mover.md_buf,
3405             session->ns_mover.md_record_size);
3406         if (n < 0) {
3407                 syslog(LOG_ERR, "Tape write error: %m.");
3408                 return (-1);
3409         }
3410 
3411         session->ns_mover.md_w_index = 0;
3412         session->ns_mover.md_position += n;
3413         return (n);
3414 }
3415 
3416 
3417 /*
3418  * ndmpd_local_write_v3
3419  *
3420  * Buffers and writes data to the tape device.
3421  * A full tape record is buffered before being written.
3422  *
3423  * Parameters:
3424  *   session    (input) - session pointer.
3425  *   data       (input) - data to be written.
3426  *   length     (input) - data length.
3427  *
3428  * Returns:
3429  *   0 - data successfully written.
3430  *  -1 - error.
3431  */
3432 int
3433 ndmpd_local_write_v3(ndmpd_session_t *session, char *data, ulong_t length)
3434 {
3435         ulong_t count = 0;
3436         ssize_t n;
3437         ulong_t len;
3438 
3439         if (session->ns_mover.md_state == NDMP_MOVER_STATE_IDLE ||
3440             session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
3441             session->ns_mover.md_state == NDMP_MOVER_STATE_HALTED) {
3442                 return (-1);
3443         }
3444 
3445         /*
3446          * A length of 0 indicates that any buffered data should be
3447          * flushed to tape.
3448          */
3449         if (length == 0) {
3450                 if (session->ns_mover.md_w_index == 0)
3451                         return (0);
3452 
3453                 (void) memset((void*)&session->ns_mover.md_buf[session->
3454                     ns_mover.md_w_index], 0, session->ns_mover.md_record_size -
3455                     session->ns_mover.md_w_index);
3456 
3457                 n = mover_tape_write_v3(session, session->ns_mover.md_buf,
3458                     session->ns_mover.md_record_size);
3459                 if (n <= 0) {
3460                         ndmpd_mover_error(session,
3461                             (n == 0 ?  NDMP_MOVER_HALT_ABORTED :
3462                             NDMP_MOVER_HALT_MEDIA_ERROR));
3463                         return (-1);
3464                 }
3465 
3466                 session->ns_mover.md_position += n;
3467                 session->ns_mover.md_data_written +=
3468                     session->ns_mover.md_w_index;
3469                 session->ns_mover.md_record_num++;
3470                 session->ns_mover.md_w_index = 0;
3471                 return (0);
3472         }
3473 
3474         /* Break the data into records. */
3475         while (count < length) {
3476                 /*
3477                  * Determine if data needs to be buffered or
3478                  * can be written directly from user supplied location.
3479                  * We can fast path the write if there is no pending
3480                  * buffered data and there is at least a full records worth
3481                  * of data to be written.
3482                  */
3483                 if (session->ns_mover.md_w_index == 0 &&
3484                     length - count >= session->ns_mover.md_record_size) {
3485                         n = mover_tape_write_v3(session, &data[count],
3486                             session->ns_mover.md_record_size);
3487                         if (n <= 0) {
3488                                 ndmpd_mover_error(session,
3489                                     (n == 0 ?  NDMP_MOVER_HALT_ABORTED :
3490                                     NDMP_MOVER_HALT_MEDIA_ERROR));
3491                                 return (-1);
3492                         }
3493 
3494                         session->ns_mover.md_position += n;
3495                         session->ns_mover.md_data_written += n;
3496                         session->ns_mover.md_record_num++;
3497                         count += n;
3498                         continue;
3499                 }
3500 
3501                 /* Buffer the data */
3502                 len = length - count;
3503                 if (len > session->ns_mover.md_record_size -
3504                     session->ns_mover.md_w_index)
3505                         len = session->ns_mover.md_record_size -
3506                             session->ns_mover.md_w_index;
3507 
3508                 (void) memcpy(&session->ns_mover.md_buf[session->
3509                     ns_mover.md_w_index], &data[count], len);
3510                 session->ns_mover.md_w_index += len;
3511                 count += len;
3512 
3513                 /* Write the buffer if its full */
3514                 if (session->ns_mover.md_w_index ==
3515                     session->ns_mover.md_record_size) {
3516                         n = mover_tape_write_v3(session,
3517                             session->ns_mover.md_buf,
3518                             session->ns_mover.md_record_size);
3519                         if (n <= 0) {
3520                                 ndmpd_mover_error(session,
3521                                     (n == 0 ?  NDMP_MOVER_HALT_ABORTED :
3522                                     NDMP_MOVER_HALT_MEDIA_ERROR));
3523                                 return (-1);
3524                         }
3525 
3526                         session->ns_mover.md_position += n;
3527                         session->ns_mover.md_data_written += n;
3528                         session->ns_mover.md_record_num++;
3529                         session->ns_mover.md_w_index = 0;
3530                 }
3531         }
3532 
3533         return (0);
3534 }
3535 
3536 
3537 /*
3538  * mover_data_read_v3
3539  *
3540  * Reads backup data from the data connection and writes the
3541  * received data to the tape device.
3542  *
3543  * Parameters:
3544  *   cookie  (input) - session pointer.
3545  *   fd      (input) - file descriptor.
3546  *   mode    (input) - select mode.
3547  *
3548  * Returns:
3549  *   void.
3550  */
3551 /*ARGSUSED*/
3552 static void
3553 mover_data_read_v3(void *cookie, int fd, ulong_t mode)
3554 {
3555         ndmpd_session_t *session = (ndmpd_session_t *)cookie;
3556         int n;
3557         ulong_t index;
3558 
3559         n = read(fd, &session->ns_mover.md_buf[session->ns_mover.md_w_index],
3560             session->ns_mover.md_record_size - session->ns_mover.md_w_index);
3561 
3562         /*
3563          * Since this function is only called when select believes data
3564          * is available to be read, a return of zero indicates the
3565          * connection has been closed.
3566          */
3567         if (n <= 0) {
3568                 if (n == 0) {
3569                         syslog(LOG_DEBUG, "Data connection closed");
3570                         ndmpd_mover_error(session,
3571                             NDMP_MOVER_HALT_CONNECT_CLOSED);
3572                 } else {
3573                         /* Socket is non-blocking, perhaps there are no data */
3574                         if (errno == EAGAIN) {
3575                                 syslog(LOG_DEBUG, "No data to read");
3576                                 return;
3577                         }
3578 
3579                         syslog(LOG_ERR,
3580                             "Failed to read from socket %d: %m", fd);
3581                         ndmpd_mover_error(session,
3582                             NDMP_MOVER_HALT_INTERNAL_ERROR);
3583                 }
3584 
3585                 /* Save the index since mover_tape_flush_v3 resets it. */
3586                 index = session->ns_mover.md_w_index;
3587 
3588                 /* Flush any buffered data to tape. */
3589                 if (mover_tape_flush_v3(session) > 0) {
3590                         session->ns_mover.md_data_written += index;
3591                         session->ns_mover.md_record_num++;
3592                 }
3593 
3594                 return;
3595         }
3596 
3597         session->ns_mover.md_w_index += n;
3598 
3599         if (session->ns_mover.md_w_index == session->ns_mover.md_record_size) {
3600                 n = mover_tape_write_v3(session, session->ns_mover.md_buf,
3601                     session->ns_mover.md_record_size);
3602                 if (n <= 0) {
3603                         ndmpd_mover_error(session,
3604                             (n == 0 ? NDMP_MOVER_HALT_ABORTED :
3605                             NDMP_MOVER_HALT_MEDIA_ERROR));
3606                         return;
3607                 }
3608 
3609                 session->ns_mover.md_position += n;
3610                 session->ns_mover.md_w_index = 0;
3611                 session->ns_mover.md_data_written += n;
3612                 session->ns_mover.md_record_num++;
3613         }
3614 }
3615 
3616 /*
3617  * mover_tape_read_v3
3618  *
3619  * Reads a data record from tape. Detects and handles EOT conditions.
3620  *
3621  * Parameters:
3622  *   session (input) - session pointer.
3623  *   data    (input) - location to read data to.
3624  *
3625  * Returns:
3626  *   0 - operation aborted.
3627  *   TAPE_READ_ERR - tape read IO error.
3628  *   TAPE_NO_WRITER_ERR - no writer is running during tape read
3629  *   otherwise - number of bytes read.
3630  */
3631 static int
3632 mover_tape_read_v3(ndmpd_session_t *session, char *data)
3633 {
3634         int pause_reason;
3635         ssize_t  n;
3636         int err;
3637         int count;
3638 
3639         count = session->ns_mover.md_record_size;
3640         while (count > 0) {
3641                 pause_reason = NDMP_MOVER_PAUSE_NA;
3642 
3643                 n = read(session->ns_tape.td_fd, data, count);
3644                 if (n < 0) {
3645                         /*
3646                          * If at beginning of file and read fails with EIO,
3647                          * then it's repeated attempt to read at EOT.
3648                          */
3649                         if (errno == EIO && tape_is_at_bof(session)) {
3650                                 pause_reason = NDMP_MOVER_PAUSE_EOM;
3651                                 NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
3652                                     ++ndmp_log_msg_id,
3653                                     "End of tape reached. Load next tape");
3654                         }
3655                         /*
3656                          * According to NDMPv4 spec preferred error code when
3657                          * trying to read from blank tape is NDMP_EOM_ERR.
3658                          */
3659                         else if (errno == EIO && tape_is_at_bot(session)) {
3660                                 syslog(LOG_ERR,
3661                                     "Blank tape detected, returning EOM");
3662                                 NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
3663                                     ++ndmp_log_msg_id,
3664                                     "Blank tape. Load another tape");
3665                                 pause_reason = NDMP_MOVER_PAUSE_EOM;
3666                         } else {
3667                                 syslog(LOG_ERR, "Tape read error: %m.");
3668                                 return (TAPE_READ_ERR);
3669                         }
3670                 } else if (n > 0) {
3671                         NS_ADD(rtape, n);
3672                         data += n;
3673                         count -= n;
3674                         session->ns_tape.td_record_count++;
3675                 } else {
3676                         if (!is_writer_running_v3(session))
3677                                 return (TAPE_NO_WRITER_ERR);
3678 
3679                         /*
3680                          * End of file or media reached. Notify client and
3681                          * wait for the client to either abort the data
3682                          * operation or continue the operation after changing
3683                          * the tape.
3684                          */
3685                         if (tape_is_at_bof(session)) {
3686                                 syslog(LOG_DEBUG, "EOT detected");
3687                                 pause_reason = NDMP_MOVER_PAUSE_EOM;
3688                                 NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
3689                                     ++ndmp_log_msg_id, "End of medium reached");
3690                         } else {
3691                                 syslog(LOG_DEBUG, "EOF detected");
3692                                 /* reposition the tape to BOT side of FM */
3693                                 fm_dance(session);
3694                                 pause_reason = NDMP_MOVER_PAUSE_EOF;
3695                                 NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
3696                                     ++ndmp_log_msg_id, "End of file reached.");
3697                         }
3698                 }
3699 
3700                 if (pause_reason != NDMP_MOVER_PAUSE_NA) {
3701                         err = mover_pause_v3(session, pause_reason);
3702 
3703                         /* Operation aborted or connection terminated? */
3704                         if (err < 0) {
3705                                 return (0);
3706                         }
3707                         /* Retry the read from new location */
3708                 }
3709         }
3710         return (session->ns_mover.md_record_size);
3711 }
3712 
3713 
3714 /*
3715  * mover_data_write_v3
3716  *
3717  * Reads backup data from the tape device and writes the
3718  * data to the data connection.
3719  * This function is called by ndmpd_select when the data connection
3720  * is ready for more data to be written.
3721  *
3722  * Parameters:
3723  *   cookie  (input) - session pointer.
3724  *   fd      (input) - file descriptor.
3725  *   mode    (input) - select mode.
3726  *
3727  * Returns:
3728  *   void.
3729  */
3730 /*ARGSUSED*/
3731 static void
3732 mover_data_write_v3(void *cookie, int fd, ulong_t mode)
3733 {
3734         ndmpd_session_t *session = (ndmpd_session_t *)cookie;
3735         int n;
3736         ulong_t len;
3737         u_longlong_t wlen;
3738         ndmp_notify_mover_paused_request pause_request;
3739 
3740         /*
3741          * If the end of the mover window has been reached,
3742          * then notify the client that a seek is needed.
3743          * Remove the file handler to prevent this function from
3744          * being called. The handler will be reinstalled in
3745          * ndmpd_mover_continue.
3746          */
3747         if (session->ns_mover.md_position >= session->ns_mover.md_window_offset
3748             + session->ns_mover.md_window_length) {
3749                 syslog(LOG_DEBUG,
3750                     "MOVER_PAUSE_SEEK(%llu)", session->ns_mover.md_position);
3751 
3752                 session->ns_mover.md_w_index = 0;
3753                 session->ns_mover.md_r_index = 0;
3754 
3755                 session->ns_mover.md_state = NDMP_MOVER_STATE_PAUSED;
3756                 session->ns_mover.md_pause_reason = NDMP_MOVER_PAUSE_SEEK;
3757                 pause_request.reason = NDMP_MOVER_PAUSE_SEEK;
3758                 pause_request.seek_position =
3759                     long_long_to_quad(session->ns_mover.md_position);
3760                 session->ns_mover.md_seek_position =
3761                     session->ns_mover.md_position;
3762 
3763                 (void) ndmpd_remove_file_handler(session, fd);
3764 
3765                 if (ndmp_send_request(session->ns_connection,
3766                     NDMP_NOTIFY_MOVER_PAUSED, NDMP_NO_ERR,
3767                     (void *)&pause_request, 0) < 0) {
3768                         syslog(LOG_DEBUG,
3769                             "Sending notify_mover_paused request");
3770                         ndmpd_mover_error(session,
3771                             NDMP_MOVER_HALT_INTERNAL_ERROR);
3772                 }
3773                 return;
3774         }
3775 
3776         /*
3777          * Read more data into the tape buffer if the buffer is empty.
3778          */
3779         if (session->ns_mover.md_w_index == 0) {
3780                 n = mover_tape_read_v3(session, session->ns_mover.md_buf);
3781 
3782                 syslog(LOG_DEBUG,
3783                     "read %u bytes from tape", n);
3784 
3785                 if (n <= 0) {
3786                         ndmpd_mover_error(session, (n == 0 ?
3787                             NDMP_MOVER_HALT_ABORTED
3788                             : NDMP_MOVER_HALT_MEDIA_ERROR));
3789                         return;
3790                 }
3791 
3792                 /*
3793                  * Discard data if the current data stream position is
3794                  * prior to the seek position. This is necessary if a seek
3795                  * request set the seek pointer to a position that is not a
3796                  * record boundary. The seek request handler can only position
3797                  * to the start of a record.
3798                  */
3799                 if (session->ns_mover.md_position <
3800                     session->ns_mover.md_seek_position) {
3801                         session->ns_mover.md_r_index =
3802                             session->ns_mover.md_seek_position -
3803                             session->ns_mover.md_position;
3804                         session->ns_mover.md_position =
3805                             session->ns_mover.md_seek_position;
3806                 }
3807 
3808                 session->ns_mover.md_w_index = n;
3809                 session->ns_mover.md_record_num++;
3810         }
3811 
3812         /*
3813          * The limit on the total amount of data to be sent can be
3814          * dictated by either the end of the mover window or the end of the
3815          * seek window.
3816          * First determine which window applies and then determine if the
3817          * send length needs to be less than a full record to avoid
3818          * exceeding the window.
3819          */
3820         if (session->ns_mover.md_position +
3821             session->ns_mover.md_bytes_left_to_read >
3822             session->ns_mover.md_window_offset +
3823             session->ns_mover.md_window_length)
3824                 wlen = session->ns_mover.md_window_offset +
3825                     session->ns_mover.md_window_length -
3826                     session->ns_mover.md_position;
3827         else
3828                 wlen = session->ns_mover.md_bytes_left_to_read;
3829 
3830         /*
3831          * Now limit the length to the amount of data in the buffer.
3832          */
3833         if (wlen > session->ns_mover.md_w_index - session->ns_mover.md_r_index)
3834                 wlen = session->ns_mover.md_w_index -
3835                     session->ns_mover.md_r_index;
3836 
3837         len = wlen & 0xffffffff;
3838 
3839         /*
3840          * Write the data to the data connection.
3841          */
3842         n = write(session->ns_mover.md_sock,
3843             &session->ns_mover.md_buf[session->ns_mover.md_r_index], len);
3844 
3845         if (n < 0) {
3846                 /* Socket is non-blocking, perhaps the write queue is full */
3847                 if (errno == EAGAIN) {
3848                         syslog(LOG_ERR, "Cannot write to socket");
3849                         return;
3850                 }
3851                 syslog(LOG_ERR, "Failed to write to socket: %m");
3852                 ndmpd_mover_error(session, NDMP_MOVER_HALT_CONNECT_CLOSED);
3853                 return;
3854         }
3855 
3856         syslog(LOG_DEBUG,
3857             "wrote %u of %u bytes to data connection position %lu r_index %lu",
3858             n, len, session->ns_mover.md_position,
3859             session->ns_mover.md_r_index);
3860 
3861         session->ns_mover.md_r_index += n;
3862         session->ns_mover.md_position += n;
3863         session->ns_mover.md_bytes_left_to_read -= n;
3864 
3865         /*
3866          * If all data in the buffer has been written,
3867          * zero the buffer indices. The next call to this function
3868          * will read more data from the tape device into the buffer.
3869          */
3870         if (session->ns_mover.md_r_index == session->ns_mover.md_w_index) {
3871                 session->ns_mover.md_r_index = 0;
3872                 session->ns_mover.md_w_index = 0;
3873         }
3874 
3875         /*
3876          * If the read limit has been reached,
3877          * then remove the file handler to prevent this
3878          * function from getting called. The next mover_read request
3879          * will reinstall the handler.
3880          */
3881         if (session->ns_mover.md_bytes_left_to_read == 0)
3882                 (void) ndmpd_remove_file_handler(session, fd);
3883 }
3884 
3885 
3886 /*
3887  * accept_connection_v3
3888  *
3889  * Accept a data connection from a data server.
3890  * Called by ndmpd_select when a connection is pending on
3891  * the mover listen socket.
3892  *
3893  * Parameters:
3894  *   cookie  (input) - session pointer.
3895  *   fd      (input) - file descriptor.
3896  *   mode    (input) - select mode.
3897  *
3898  * Returns:
3899  *   void.
3900  */
3901 /*ARGSUSED*/
3902 static void
3903 accept_connection_v3(void *cookie, int fd, ulong_t mode)
3904 {
3905         ndmpd_session_t *session = (ndmpd_session_t *)cookie;
3906         int from_len;
3907         struct sockaddr_in from;
3908 
3909         from_len = sizeof (from);
3910         session->ns_mover.md_sock = accept(fd, (struct sockaddr *)&from,
3911             &from_len);
3912 
3913         syslog(LOG_DEBUG, "sin: port %d addr %s", ntohs(from.sin_port),
3914             inet_ntoa(IN_ADDR(from.sin_addr.s_addr)));
3915 
3916         (void) ndmpd_remove_file_handler(session, fd);
3917         (void) close(session->ns_mover.md_listen_sock);
3918         session->ns_mover.md_listen_sock = -1;
3919 
3920         if (session->ns_mover.md_sock < 0) {
3921                 syslog(LOG_DEBUG, "Accept error: %m");
3922                 ndmpd_mover_error(session, NDMP_MOVER_HALT_CONNECT_ERROR);
3923                 return;
3924         }
3925 
3926         /*
3927          * Save the peer address.
3928          */
3929         session->ns_mover.md_data_addr.tcp_ip_v3 = from.sin_addr.s_addr;
3930         session->ns_mover.md_data_addr.tcp_port_v3 = ntohs(from.sin_port);
3931 
3932         /* Set the parameter of the new socket */
3933         set_socket_options(session->ns_mover.md_sock);
3934 
3935         /*
3936          * Backup/restore is handled by a callback called from main event loop,
3937          * which reads/writes data to md_sock socket. IO on socket must be
3938          * non-blocking, otherwise ndmpd would be unable to process other
3939          * incoming requests.
3940          */
3941         if (!set_socket_nonblock(session->ns_mover.md_sock)) {
3942                 syslog(LOG_ERR, "Could not set non-blocking mode "
3943                     "on socket: %m");
3944                 ndmpd_mover_error(session, NDMP_MOVER_HALT_INTERNAL_ERROR);
3945                 return;
3946         }
3947 
3948         syslog(LOG_DEBUG, "sock fd: %d", session->ns_mover.md_sock);
3949 
3950         if (session->ns_mover.md_mode == NDMP_MOVER_MODE_READ) {
3951                 if (ndmpd_add_file_handler(session, (void*)session,
3952                     session->ns_mover.md_sock, NDMPD_SELECT_MODE_READ,
3953                     HC_MOVER, mover_data_read_v3) < 0) {
3954                         ndmpd_mover_error(session,
3955                             NDMP_MOVER_HALT_INTERNAL_ERROR);
3956                         return;
3957                 }
3958                 syslog(LOG_DEBUG, "Backup connection established by %s:%d",
3959                     inet_ntoa(IN_ADDR(from.sin_addr.s_addr)),
3960                     ntohs(from.sin_port));
3961         } else {
3962                 syslog(LOG_DEBUG, "Restore connection established by %s:%d",
3963                     inet_ntoa(IN_ADDR(from.sin_addr.s_addr)),
3964                     ntohs(from.sin_port));
3965         }
3966 
3967         session->ns_mover.md_state = NDMP_MOVER_STATE_ACTIVE;
3968 }
3969 
3970 
3971 /*
3972  * create_listen_socket_v3
3973  *
3974  * Creates a socket for listening for accepting data connections.
3975  *
3976  * Parameters:
3977  *   session (input)  - session pointer.
3978  *   addr    (output) - location to store address of socket.
3979  *   port    (output) - location to store port of socket.
3980  *
3981  * Returns:
3982  *   0 - success.
3983  *  -1 - error.
3984  */
3985 static int
3986 create_listen_socket_v3(ndmpd_session_t *session, ulong_t *addr, ushort_t *port)
3987 {
3988         session->ns_mover.md_listen_sock = ndmp_create_socket(addr, port);
3989         if (session->ns_mover.md_listen_sock < 0)
3990                 return (-1);
3991 
3992         /*
3993          * Add a file handler for the listen socket.
3994          * ndmpd_select will call accept_connection when a
3995          * connection is ready to be accepted.
3996          */
3997         if (ndmpd_add_file_handler(session, (void *) session,
3998             session->ns_mover.md_listen_sock, NDMPD_SELECT_MODE_READ, HC_MOVER,
3999             accept_connection_v3) < 0) {
4000                 (void) close(session->ns_mover.md_listen_sock);
4001                 session->ns_mover.md_listen_sock = -1;
4002                 return (-1);
4003         }
4004         syslog(LOG_DEBUG, "IP %s port %d",
4005             inet_ntoa(*(struct in_addr *)addr), ntohs(*port));
4006         return (0);
4007 }
4008 
4009 
4010 /*
4011  * mover_connect_sock
4012  *
4013  * Connect the mover to the specified address
4014  *
4015  * Parameters:
4016  *   session (input)  - session pointer.
4017  *   mode    (input)  - mover mode.
4018  *   addr    (output) - location to store address of socket.
4019  *   port    (output) - location to store port of socket.
4020  *
4021  * Returns:
4022  *   error code.
4023  */
4024 static ndmp_error
4025 mover_connect_sock(ndmpd_session_t *session, ndmp_mover_mode mode,
4026     ulong_t addr, ushort_t port)
4027 {
4028         int sock;
4029 
4030         sock = ndmp_connect_sock_v3(addr, port);
4031         if (sock < 0)
4032                 return (NDMP_CONNECT_ERR);
4033 
4034         /*
4035          * Backup/restore is handled by a callback called from main event loop,
4036          * which reads/writes data to md_sock socket. IO on socket must be
4037          * non-blocking, otherwise ndmpd would be unable to process other
4038          * incoming requests.
4039          */
4040         if (!set_socket_nonblock(sock)) {
4041                 syslog(LOG_ERR, "Could not set non-blocking mode "
4042                     "on socket: %m");
4043                 (void) close(sock);
4044                 return (NDMP_CONNECT_ERR);
4045         }
4046 
4047         if (mode == NDMP_MOVER_MODE_READ) {
4048                 if (ndmpd_add_file_handler(session, (void*)session, sock,
4049                     NDMPD_SELECT_MODE_READ, HC_MOVER, mover_data_read_v3) < 0) {
4050                         (void) close(sock);
4051                         return (NDMP_CONNECT_ERR);
4052                 }
4053         }
4054         session->ns_mover.md_sock = sock;
4055         session->ns_mover.md_data_addr.addr_type = NDMP_ADDR_TCP;
4056         session->ns_mover.md_data_addr.tcp_ip_v3 = ntohl(addr);
4057         session->ns_mover.md_data_addr.tcp_port_v3 = port;
4058         return (NDMP_NO_ERR);
4059 }
4060 
4061 
4062 /*
4063  * ndmpd_local_read_v3
4064  *
4065  * Reads data from the local tape device.
4066  * Full tape records are read and buffered.
4067  *
4068  * Parameters:
4069  *   session (input) - session pointer.
4070  *   data    (input) - location to store data.
4071  *   length  (input) - data length.
4072  *
4073  * Returns:
4074  *   1 - no read error but no writer running
4075  *   0 - data successfully read.
4076  *  -1 - error.
4077  */
4078 int
4079 ndmpd_local_read_v3(ndmpd_session_t *session, char *data, ulong_t length)
4080 {
4081         ulong_t count;
4082         ulong_t len;
4083         ssize_t n;
4084 
4085         count = 0;
4086         if (session->ns_mover.md_state == NDMP_MOVER_STATE_IDLE ||
4087             session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
4088             session->ns_mover.md_state == NDMP_MOVER_STATE_HALTED) {
4089                 return (-1);
4090         }
4091 
4092         /*
4093          * Automatically increase the seek window if necessary.
4094          * This is needed in the event the module attempts to read
4095          * past a seek window set via a prior call to ndmpd_seek() or
4096          * the module has not issued a seek. If no seek was issued then
4097          * pretend that a seek was issued to read the entire tape.
4098          */
4099         if (length > session->ns_mover.md_bytes_left_to_read) {
4100                 /* ndmpd_seek() never called? */
4101                 if (session->ns_data.dd_read_length == 0) {
4102                         session->ns_mover.md_bytes_left_to_read = ~0LL;
4103                         session->ns_data.dd_read_offset = 0LL;
4104                         session->ns_data.dd_read_length = ~0LL;
4105                 } else {
4106                         session->ns_mover.md_bytes_left_to_read = length;
4107                         session->ns_data.dd_read_offset =
4108                             session->ns_mover.md_position;
4109                         session->ns_data.dd_read_length = length;
4110                 }
4111         }
4112 
4113         /*
4114          * Read as many records as necessary to satisfy the request.
4115          */
4116         while (count < length) {
4117                 /*
4118                  * If the end of the mover window has been reached,
4119                  * then notify the client that a new data window is needed.
4120                  */
4121                 if (session->ns_mover.md_position >=
4122                     session->ns_mover.md_window_offset +
4123                     session->ns_mover.md_window_length) {
4124                         if (mover_pause_v3(session,
4125                             NDMP_MOVER_PAUSE_SEEK) < 0) {
4126                                 ndmpd_mover_error(session,
4127                                     NDMP_MOVER_HALT_INTERNAL_ERROR);
4128                                 return (-1);
4129                         }
4130                         continue;
4131                 }
4132 
4133                 len = length - count;
4134 
4135                 /*
4136                  * Prevent reading past the end of the window.
4137                  */
4138                 if (len > session->ns_mover.md_window_offset +
4139                     session->ns_mover.md_window_length -
4140                     session->ns_mover.md_position)
4141                         len = session->ns_mover.md_window_offset +
4142                             session->ns_mover.md_window_length -
4143                             session->ns_mover.md_position;
4144 
4145                 /*
4146                  * Copy from the data buffer first.
4147                  */
4148                 if (session->ns_mover.md_w_index -
4149                     session->ns_mover.md_r_index != 0) {
4150                         /*
4151                          * Limit the copy to the amount of data in the buffer.
4152                          */
4153                         if (len > session->ns_mover.md_w_index -
4154                             session->ns_mover.md_r_index)
4155                                 len = session->ns_mover.md_w_index -
4156                                     session->ns_mover.md_r_index;
4157                         (void) memcpy((void*)&data[count],
4158                             &session->ns_mover.md_buf[session->
4159                             ns_mover.md_r_index], len);
4160                         count += len;
4161                         session->ns_mover.md_r_index += len;
4162                         session->ns_mover.md_bytes_left_to_read -= len;
4163                         session->ns_mover.md_position += len;
4164                         continue;
4165                 }
4166 
4167                 /*
4168                  * Determine if data needs to be buffered or
4169                  * can be read directly to user supplied location.
4170                  * We can fast path the read if at least a full record
4171                  * needs to be read and there is no seek pending.
4172                  * This is done to eliminate a buffer copy.
4173                  */
4174                 if (len >= session->ns_mover.md_record_size &&
4175                     session->ns_mover.md_position >=
4176                     session->ns_mover.md_seek_position) {
4177                         n = mover_tape_read_v3(session, &data[count]);
4178                         if (n <= 0) {
4179                                 if (n == TAPE_NO_WRITER_ERR)
4180                                         return (1);
4181 
4182                                 ndmpd_mover_error(session,
4183                                     (n == 0 ? NDMP_MOVER_HALT_ABORTED :
4184                                     NDMP_MOVER_HALT_MEDIA_ERROR));
4185                                 return ((n == 0) ? 1 : -1);
4186                         }
4187 
4188                         count += n;
4189                         session->ns_mover.md_bytes_left_to_read -= n;
4190                         session->ns_mover.md_position += n;
4191                         session->ns_mover.md_record_num++;
4192                         continue;
4193                 }
4194 
4195                 /* Read the next record into the buffer. */
4196                 n = mover_tape_read_v3(session, session->ns_mover.md_buf);
4197                 if (n <= 0) {
4198                         if (n == TAPE_NO_WRITER_ERR)
4199                                 return (1);
4200 
4201                         ndmpd_mover_error(session,
4202                             (n == 0 ? NDMP_MOVER_HALT_ABORTED :
4203                             NDMP_MOVER_HALT_MEDIA_ERROR));
4204                         return ((n == 0) ? 1 : -1);
4205                 }
4206 
4207                 session->ns_mover.md_w_index = n;
4208                 session->ns_mover.md_r_index = 0;
4209                 session->ns_mover.md_record_num++;
4210 
4211                 /*
4212                  * Discard data if the current data stream position is
4213                  * prior to the seek position. This is necessary if a seek
4214                  * request set the seek pointer to a position that is not a
4215                  * record boundary. The seek request handler can only position
4216                  * to the start of a record.
4217                  */
4218                 if (session->ns_mover.md_position <
4219                     session->ns_mover.md_seek_position) {
4220                         session->ns_mover.md_r_index =
4221                             session->ns_mover.md_seek_position -
4222                             session->ns_mover.md_position;
4223                         session->ns_mover.md_position =
4224                             session->ns_mover.md_seek_position;
4225                 }
4226         }
4227 
4228         return (0);
4229 }