1 /*
   2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * BSD 3 Clause License
   8  *
   9  * Copyright (c) 2007, The Storage Networking Industry Association.
  10  *
  11  * Redistribution and use in source and binary forms, with or without
  12  * modification, are permitted provided that the following conditions
  13  * are met:
  14  *      - Redistributions of source code must retain the above copyright
  15  *        notice, this list of conditions and the following disclaimer.
  16  *
  17  *      - Redistributions in binary form must reproduce the above copyright
  18  *        notice, this list of conditions and the following disclaimer in
  19  *        the documentation and/or other materials provided with the
  20  *        distribution.
  21  *
  22  *      - Neither the name of The Storage Networking Industry Association (SNIA)
  23  *        nor the names of its contributors may be used to endorse or promote
  24  *        products derived from this software without specific prior written
  25  *        permission.
  26  *
  27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  37  * POSSIBILITY OF SUCH DAMAGE.
  38  */
  39 /* Copyright (c) 2007, The Storage Networking Industry Association. */
  40 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
  41 /* Copyright 2017 Nexenta Systems, Inc. All rights reserved. */
  42 
  43 #include <sys/types.h>
  44 #include <syslog.h>
  45 #include <ctype.h>
  46 #include <errno.h>
  47 #include <fcntl.h>
  48 #include <stdlib.h>
  49 #include "ndmpd_common.h"
  50 #include "ndmpd.h"
  51 #include <string.h>
  52 #include <sys/scsi/impl/uscsi.h>
  53 #include <sys/scsi/scsi.h>
  54 
  55 static void scsi_open_send_reply(ndmp_connection_t *connection, int err);
  56 static void common_open(ndmp_connection_t *connection, char *devname);
  57 static void common_set_target(ndmp_connection_t *connection, char *device,
  58     ushort_t controller, ushort_t sid, ushort_t lun);
  59 
  60 
  61 /*
  62  * ************************************************************************
  63  * NDMP V2 HANDLERS
  64  * ************************************************************************
  65  */
  66 
  67 /*
  68  * ndmpd_scsi_open_v2
  69  *
  70  * This handler opens the specified SCSI device.
  71  *
  72  * Parameters:
  73  *   connection (input) - connection handle.
  74  *   body       (input) - request message body.
  75  *
  76  * Returns:
  77  *   void
  78  */
  79 void
  80 ndmpd_scsi_open_v2(ndmp_connection_t *connection, void *body)
  81 {
  82         ndmp_scsi_open_request_v2 *request = (ndmp_scsi_open_request_v2 *)body;
  83 
  84         common_open(connection, request->device.name);
  85 }
  86 
  87 
  88 /*
  89  * ndmpd_scsi_close_v2
  90  *
  91  * This handler closes the currently open SCSI device.
  92  *
  93  * Parameters:
  94  *   connection (input) - connection handle.
  95  *   body       (input) - request message body.
  96  *
  97  * Returns:
  98  *   void
  99  */
 100 /*ARGSUSED*/
 101 void
 102 ndmpd_scsi_close_v2(ndmp_connection_t *connection, void *body)
 103 {
 104         ndmp_scsi_close_reply reply;
 105         ndmpd_session_t *session = ndmp_get_client_data(connection);
 106 
 107         if (session->ns_scsi.sd_is_open == -1) {
 108                 syslog(LOG_ERR, "SCSI device is not open.");
 109                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 110                 ndmp_send_reply(connection, (void *) &reply,
 111                     "sending scsi_close reply");
 112                 return;
 113         }
 114         (void) ndmp_open_list_del(session->ns_scsi.sd_adapter_name,
 115             session->ns_scsi.sd_sid,
 116             session->ns_scsi.sd_lun);
 117         (void) close(session->ns_scsi.sd_devid);
 118 
 119         session->ns_scsi.sd_is_open = -1;
 120         session->ns_scsi.sd_devid = -1;
 121         session->ns_scsi.sd_sid = 0;
 122         session->ns_scsi.sd_lun = 0;
 123         session->ns_scsi.sd_valid_target_set = FALSE;
 124         (void) memset(session->ns_scsi.sd_adapter_name, 0,
 125             sizeof (session->ns_scsi.sd_adapter_name));
 126 
 127         reply.error = NDMP_NO_ERR;
 128         ndmp_send_reply(connection, (void *) &reply,
 129             "sending scsi_close reply");
 130 }
 131 
 132 
 133 /*
 134  * ndmpd_scsi_get_state_v2
 135  *
 136  * This handler returns state information for the currently open SCSI device.
 137  * Since the implementation only supports the opening of a specific SCSI
 138  * device, as opposed to a device that can talk to multiple SCSI targets,
 139  * this request is not supported. This request is only appropriate for
 140  * implementations that support device files that can target multiple
 141  * SCSI devices.
 142  *
 143  * Parameters:
 144  *   connection (input) - connection handle.
 145  *   body       (input) - request message body.
 146  *
 147  * Returns:
 148  *   void
 149  */
 150 /*ARGSUSED*/
 151 void
 152 ndmpd_scsi_get_state_v2(ndmp_connection_t *connection, void *body)
 153 {
 154         ndmp_scsi_get_state_reply reply;
 155         ndmpd_session_t *session = ndmp_get_client_data(connection);
 156 
 157         if (session->ns_scsi.sd_is_open == -1)
 158                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 159         else if (!session->ns_scsi.sd_valid_target_set) {
 160                 reply.error = NDMP_NO_ERR;
 161                 reply.target_controller = -1;
 162                 reply.target_id = -1;
 163                 reply.target_lun = -1;
 164         } else {
 165                 reply.error = NDMP_NO_ERR;
 166                 reply.target_controller = 0;
 167                 reply.target_id = session->ns_scsi.sd_sid;
 168                 reply.target_lun = session->ns_scsi.sd_lun;
 169         }
 170 
 171         ndmp_send_reply(connection, (void *) &reply,
 172             "sending scsi_get_state reply");
 173 }
 174 
 175 
 176 /*
 177  * ndmpd_scsi_set_target_v2
 178  *
 179  * This handler sets the SCSI target of the SCSI device.
 180  * It is only valid to use this request if the opened SCSI device
 181  * is capable of talking to multiple SCSI targets.
 182  * Since the implementation only supports the opening of a specific SCSI
 183  * device, as opposed to a device that can talk to multiple SCSI targets,
 184  * this request is not supported. This request is only appropriate for
 185  * implementations that support device files that can target multiple
 186  * SCSI devices.
 187  *
 188  * Parameters:
 189  *   connection (input) - connection handle.
 190  *   body       (input) - request message body.
 191  *
 192  * Returns:
 193  *   void
 194  */
 195 void
 196 ndmpd_scsi_set_target_v2(ndmp_connection_t *connection, void *body)
 197 {
 198         ndmp_scsi_set_target_request_v2 *request;
 199 
 200         request = (ndmp_scsi_set_target_request_v2 *) body;
 201 
 202         common_set_target(connection, request->device.name,
 203             request->target_controller, request->target_id,
 204             request->target_lun);
 205 }
 206 
 207 
 208 /*
 209  * ndmpd_scsi_reset_device_v2
 210  *
 211  * This handler resets the currently targeted SCSI device.
 212  *
 213  * Parameters:
 214  *   connection (input) - connection handle.
 215  *   body       (input) - request message body.
 216  *
 217  * Returns:
 218  *   void
 219  */
 220 /*ARGSUSED*/
 221 void
 222 ndmpd_scsi_reset_device_v2(ndmp_connection_t *connection, void *body)
 223 {
 224         ndmp_scsi_reset_device_reply reply;
 225 
 226 
 227         ndmpd_session_t *session = ndmp_get_client_data(connection);
 228         struct uscsi_cmd  cmd;
 229 
 230         if (session->ns_scsi.sd_devid == -1) {
 231                 syslog(LOG_ERR, "SCSI device is not open.");
 232                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 233         } else {
 234                 reply.error = NDMP_NO_ERR;
 235                 (void) memset((void*)&cmd, 0, sizeof (cmd));
 236                 cmd.uscsi_flags |= USCSI_RESET;
 237                 if (ioctl(session->ns_scsi.sd_devid, USCSICMD, &cmd) < 0) {
 238                         syslog(LOG_ERR, "USCSI reset failed: %m.");
 239                         reply.error = NDMP_IO_ERR;
 240                 }
 241         }
 242 
 243         ndmp_send_reply(connection, (void *) &reply,
 244             "sending scsi_reset_device reply");
 245 }
 246 
 247 
 248 /*
 249  * ndmpd_scsi_reset_bus_v2
 250  *
 251  * This handler resets the currently targeted SCSI bus.
 252  *
 253  * Request not yet supported.
 254  *
 255  * Parameters:
 256  *   connection (input) - connection handle.
 257  *   body       (input) - request message body.
 258  *
 259  * Returns:
 260  *   void
 261  */
 262 /*ARGSUSED*/
 263 void
 264 ndmpd_scsi_reset_bus_v2(ndmp_connection_t *connection, void *body)
 265 {
 266         ndmp_scsi_reset_bus_reply reply;
 267 
 268         reply.error = NDMP_NOT_SUPPORTED_ERR;
 269 
 270         ndmp_send_reply(connection, (void *) &reply,
 271             "sending scsi_reset_bus reply");
 272 }
 273 
 274 
 275 /*
 276  * ndmpd_scsi_execute_cdb_v2
 277  *
 278  * This handler sends the CDB to the currently targeted SCSI device.
 279  *
 280  * Parameters:
 281  *   connection (input) - connection handle.
 282  *   body       (input) - request message body.
 283  *
 284  * Returns:
 285  *   void
 286  */
 287 void
 288 ndmpd_scsi_execute_cdb_v2(ndmp_connection_t *connection, void *body)
 289 {
 290         ndmp_execute_cdb_request *request = (ndmp_execute_cdb_request *) body;
 291         ndmp_execute_cdb_reply reply;
 292         ndmpd_session_t *session = ndmp_get_client_data(connection);
 293 
 294         if (session->ns_scsi.sd_is_open == -1 ||
 295             !session->ns_scsi.sd_valid_target_set) {
 296                 (void) memset((void *) &reply, 0, sizeof (reply));
 297 
 298                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 299                 ndmp_send_reply(connection, (void *) &reply,
 300                     "sending scsi_execute_cdb reply");
 301         } else {
 302                 ndmp_execute_cdb(session, session->ns_scsi.sd_adapter_name,
 303                     session->ns_scsi.sd_sid, session->ns_scsi.sd_lun, request);
 304         }
 305 }
 306 
 307 
 308 /*
 309  * ************************************************************************
 310  * NDMP V3 HANDLERS
 311  * ************************************************************************
 312  */
 313 
 314 /*
 315  * ndmpd_scsi_open_v3
 316  *
 317  * This handler opens the specified SCSI device.
 318  *
 319  * Parameters:
 320  *   connection (input) - connection handle.
 321  *   body       (input) - request message body.
 322  *
 323  * Returns:
 324  *   void
 325  */
 326 void
 327 ndmpd_scsi_open_v3(ndmp_connection_t *connection, void *body)
 328 {
 329         ndmp_scsi_open_request_v3 *request = (ndmp_scsi_open_request_v3 *)body;
 330 
 331         common_open(connection, request->device);
 332 }
 333 
 334 
 335 /*
 336  * ndmpd_scsi_set_target_v3
 337  *
 338  * This handler sets the SCSI target of the SCSI device.
 339  * It is only valid to use this request if the opened SCSI device
 340  * is capable of talking to multiple SCSI targets.
 341  *
 342  * Parameters:
 343  *   connection (input) - connection handle.
 344  *   body       (input) - request message body.
 345  *
 346  * Returns:
 347  *   void
 348  */
 349 void
 350 ndmpd_scsi_set_target_v3(ndmp_connection_t *connection, void *body)
 351 {
 352         ndmp_scsi_set_target_request_v3 *request;
 353 
 354         request = (ndmp_scsi_set_target_request_v3 *) body;
 355 
 356         common_set_target(connection, request->device,
 357             request->target_controller, request->target_id,
 358             request->target_lun);
 359 }
 360 
 361 
 362 /*
 363  * ************************************************************************
 364  * NDMP V4 HANDLERS
 365  * ************************************************************************
 366  */
 367 
 368 /*
 369  * ************************************************************************
 370  * LOCALS
 371  * ************************************************************************
 372  */
 373 
 374 
 375 /*
 376  * scsi_open_send_reply
 377  *
 378  * Send a reply for SCSI open command
 379  *
 380  * Parameters:
 381  *   connection (input) - connection handle.
 382  *   err        (input) - ndmp error code
 383  *
 384  * Returns:
 385  *   void
 386  */
 387 static void
 388 scsi_open_send_reply(ndmp_connection_t *connection, int err)
 389 {
 390         ndmp_scsi_open_reply reply;
 391 
 392         reply.error = err;
 393         ndmp_send_reply(connection, (void *) &reply, "sending scsi_open reply");
 394 }
 395 
 396 
 397 /*
 398  * common_open
 399  *
 400  * Common SCSI open function for all NDMP versions
 401  *
 402  * Parameters:
 403  *   connection (input) - connection handle.
 404  *   devname (input) - device name to open.
 405  *
 406  * Returns:
 407  *   void
 408  */
 409 static void
 410 common_open(ndmp_connection_t *connection, char *devname)
 411 {
 412         ndmpd_session_t *session = ndmp_get_client_data(connection);
 413         char adptnm[SCSI_MAX_NAME];
 414         int sid, lun;
 415         int err;
 416         scsi_adapter_t *sa;
 417         int devid = -1;
 418 
 419         err = NDMP_NO_ERR;
 420 
 421         if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) {
 422                 err = NDMP_DEVICE_OPENED_ERR;
 423         } else if ((sa = scsi_get_adapter(0)) != NULL) {
 424                 (void) strlcpy(adptnm, devname, SCSI_MAX_NAME-2);
 425                 adptnm[SCSI_MAX_NAME-1] = '\0';
 426                 sid = lun = -1;
 427 
 428                 scsi_find_sid_lun(sa, devname, &sid, &lun);
 429                 if (ndmp_open_list_find(devname, sid, lun) == NULL &&
 430                     (devid = open(devname, O_RDWR | O_NDELAY)) < 0) {
 431                         syslog(LOG_ERR, "Failed to open device %s: %m.",
 432                             devname);
 433                         err = NDMP_NO_DEVICE_ERR;
 434                 }
 435         } else {
 436                 syslog(LOG_ERR, "%s: No such SCSI adapter.", devname);
 437                 err = NDMP_NO_DEVICE_ERR;
 438         }
 439 
 440         if (err != NDMP_NO_ERR) {
 441                 scsi_open_send_reply(connection, err);
 442                 return;
 443         }
 444 
 445         switch (ndmp_open_list_add(connection, adptnm, sid, lun, devid)) {
 446         case 0:
 447                 /* OK */
 448                 break;
 449         case EBUSY:
 450                 err = NDMP_DEVICE_BUSY_ERR;
 451                 break;
 452         case ENOMEM:
 453                 err = NDMP_NO_MEM_ERR;
 454                 break;
 455         default:
 456                 err = NDMP_IO_ERR;
 457         }
 458         if (err != NDMP_NO_ERR) {
 459                 scsi_open_send_reply(connection, err);
 460                 return;
 461         }
 462 
 463         (void) strlcpy(session->ns_scsi.sd_adapter_name, adptnm, SCSI_MAX_NAME);
 464         session->ns_scsi.sd_is_open = 1;
 465         session->ns_scsi.sd_devid = devid;
 466         if (sid != -1) {
 467                 session->ns_scsi.sd_sid = sid;
 468                 session->ns_scsi.sd_lun = lun;
 469                 session->ns_scsi.sd_valid_target_set = TRUE;
 470         } else {
 471                 session->ns_scsi.sd_sid = session->ns_scsi.sd_lun = -1;
 472                 session->ns_scsi.sd_valid_target_set = FALSE;
 473         }
 474 
 475         scsi_open_send_reply(connection, err);
 476 }
 477 
 478 
 479 /*
 480  * common_set_target
 481  *
 482  * Set the SCSI target (SCSI number, LUN number, controller number)
 483  *
 484  * Parameters:
 485  *   connection (input) - connection handle.
 486  *   device (input) - device name.
 487  *   controller (input) - controller number.
 488  *   sid (input) - SCSI target ID.
 489  *   lun (input) - LUN number.
 490  *
 491  * Returns:
 492  *   0: on success
 493  *  -1: otherwise
 494  */
 495 /*ARGSUSED*/
 496 static void
 497 common_set_target(ndmp_connection_t *connection, char *device,
 498     ushort_t controller, ushort_t sid, ushort_t lun)
 499 {
 500         ndmp_scsi_set_target_reply reply;
 501         ndmpd_session_t *session = ndmp_get_client_data(connection);
 502         int type;
 503 
 504         reply.error = NDMP_NO_ERR;
 505 
 506         if (session->ns_scsi.sd_is_open == -1) {
 507                 reply.error = NDMP_DEV_NOT_OPEN_ERR;
 508         } else if (!scsi_dev_exists(session->ns_scsi.sd_adapter_name, sid,
 509             lun)) {
 510                 syslog(LOG_ERR, "No such SCSI device: target %d lun %d.",
 511                     sid, lun);
 512                 reply.error = NDMP_NO_DEVICE_ERR;
 513         } else {
 514                 type = scsi_get_devtype(session->ns_scsi.sd_adapter_name, sid,
 515                     lun);
 516                 if (type != DTYPE_SEQUENTIAL && type != DTYPE_CHANGER) {
 517                         syslog(LOG_ERR,
 518                             "Not a tape or robot device: target %d lun %d.",
 519                             sid, lun);
 520                         reply.error = NDMP_ILLEGAL_ARGS_ERR;
 521                 }
 522         }
 523 
 524         if (reply.error != NDMP_NO_ERR) {
 525                 ndmp_send_reply(connection, (void *) &reply,
 526                     "sending scsi_set_target reply");
 527                 return;
 528         }
 529 
 530         /*
 531          * The open_list must be updated if the SID or LUN are going to be
 532          * changed.  Close uses the same SID & LUN for removing the entry
 533          * from the open_list.
 534          */
 535         if (sid != session->ns_scsi.sd_sid || lun != session->ns_scsi.sd_lun) {
 536                 switch (ndmp_open_list_add(connection,
 537                     session->ns_scsi.sd_adapter_name, sid, lun, 0)) {
 538                 case 0:
 539                         (void) ndmp_open_list_del(session->
 540                             ns_scsi.sd_adapter_name, session->ns_scsi.sd_sid,
 541                             session->ns_scsi.sd_lun);
 542                         break;
 543                 case EBUSY:
 544                         reply.error = NDMP_DEVICE_BUSY_ERR;
 545                         break;
 546                 case ENOMEM:
 547                         reply.error = NDMP_NO_MEM_ERR;
 548                         break;
 549                 default:
 550                         reply.error = NDMP_IO_ERR;
 551                 }
 552         }
 553 
 554         if (reply.error == NDMP_NO_ERR) {
 555                 session->ns_scsi.sd_sid = sid;
 556                 session->ns_scsi.sd_lun = lun;
 557                 session->ns_scsi.sd_valid_target_set = TRUE;
 558         }
 559 
 560         ndmp_send_reply(connection, (void *) &reply,
 561             "sending scsi_set_target reply");
 562 }
 563 
 564 /*
 565  * scsi_find_sid_lun
 566  *
 567  * gets the adapter, and returns the sid and lun number
 568  */
 569 void
 570 scsi_find_sid_lun(scsi_adapter_t *sa, char *devname, int *sid, int *lun)
 571 {
 572         scsi_link_t *sl;
 573         char *name;
 574 
 575         for (sl = sa->sa_link_head.sl_next; sl && sl != &sa->sa_link_head;
 576             sl = sl->sl_next) {
 577                 name = sasd_slink_name(sl);
 578                 if (strcmp(devname, name) == 0) {
 579                         *sid = sl->sl_sid;
 580                         *lun = sl->sl_lun;
 581                         return;
 582                 }
 583         }
 584 
 585         *sid = -1;
 586         *lun = -1;
 587 }