xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c (revision 9ee94b97)
12654012fSReza Sabdar /*
286c48bbfSReza Sabdar  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
32654012fSReza Sabdar  * Use is subject to license terms.
42654012fSReza Sabdar  */
52654012fSReza Sabdar /*
62654012fSReza Sabdar  * BSD 3 Clause License
72654012fSReza Sabdar  *
82654012fSReza Sabdar  * Copyright (c) 2007, The Storage Networking Industry Association.
92654012fSReza Sabdar  *
102654012fSReza Sabdar  * Redistribution and use in source and binary forms, with or without
112654012fSReza Sabdar  * modification, are permitted provided that the following conditions
122654012fSReza Sabdar  * are met:
132654012fSReza Sabdar  * 	- Redistributions of source code must retain the above copyright
142654012fSReza Sabdar  *	  notice, this list of conditions and the following disclaimer.
152654012fSReza Sabdar  *
162654012fSReza Sabdar  * 	- Redistributions in binary form must reproduce the above copyright
172654012fSReza Sabdar  *	  notice, this list of conditions and the following disclaimer in
182654012fSReza Sabdar  *	  the documentation and/or other materials provided with the
192654012fSReza Sabdar  *	  distribution.
202654012fSReza Sabdar  *
212654012fSReza Sabdar  *	- Neither the name of The Storage Networking Industry Association (SNIA)
222654012fSReza Sabdar  *	  nor the names of its contributors may be used to endorse or promote
232654012fSReza Sabdar  *	  products derived from this software without specific prior written
242654012fSReza Sabdar  *	  permission.
252654012fSReza Sabdar  *
262654012fSReza Sabdar  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
272654012fSReza Sabdar  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
282654012fSReza Sabdar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
292654012fSReza Sabdar  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
302654012fSReza Sabdar  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
312654012fSReza Sabdar  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
322654012fSReza Sabdar  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
332654012fSReza Sabdar  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
342654012fSReza Sabdar  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
352654012fSReza Sabdar  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
362654012fSReza Sabdar  * POSSIBILITY OF SUCH DAMAGE.
372654012fSReza Sabdar  */
382654012fSReza Sabdar /* Copyright (c) 2007, The Storage Networking Industry Association. */
392654012fSReza Sabdar /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
40*9ee94b97SJan Kryl /* Copyright 2014 Nexenta Systems, Inc.  All rights reserved. */
412654012fSReza Sabdar 
422654012fSReza Sabdar #include <sys/param.h>
432654012fSReza Sabdar #include <fcntl.h>
442654012fSReza Sabdar #include <sys/mtio.h>
452654012fSReza Sabdar #include <errno.h>
462654012fSReza Sabdar #include <stdio.h>
472654012fSReza Sabdar #include <string.h>
482654012fSReza Sabdar #include <unistd.h>
492654012fSReza Sabdar #include "ndmpd_common.h"
502654012fSReza Sabdar #include "ndmpd.h"
512654012fSReza Sabdar 
522654012fSReza Sabdar static void tape_open_send_reply(ndmp_connection_t *connection, int err);
532654012fSReza Sabdar static void unbuffered_read(ndmpd_session_t *session, char *buf, long wanted,
542654012fSReza Sabdar     ndmp_tape_read_reply *reply);
552654012fSReza Sabdar static boolean_t validmode(int mode);
562654012fSReza Sabdar static void common_tape_open(ndmp_connection_t *connection, char *devname,
572654012fSReza Sabdar     int ndmpmode);
582654012fSReza Sabdar static void common_tape_close(ndmp_connection_t *connection);
592654012fSReza Sabdar 
602654012fSReza Sabdar /*
612654012fSReza Sabdar  * Configurable delay & time when the tape is
622654012fSReza Sabdar  * busy during opening the tape.
632654012fSReza Sabdar  */
642654012fSReza Sabdar int ndmp_tape_open_retries = 5;
652654012fSReza Sabdar int ndmp_tape_open_delay = 1000;
662654012fSReza Sabdar 
67*9ee94b97SJan Kryl /*
68*9ee94b97SJan Kryl  * A few words about EOT (end-of-tape) and EOM handling on tapes with SVR4
69*9ee94b97SJan Kryl  * semantic:
70*9ee94b97SJan Kryl  *
71*9ee94b97SJan Kryl  * We adhere to terminology as used in st driver.  EOT means end of recorded
72*9ee94b97SJan Kryl  * data on a tape. This is different from EOM (somewhere referred to as LEOT)
73*9ee94b97SJan Kryl  * which is the end of tape medium. EOT is meaningful only for reads while EOM
74*9ee94b97SJan Kryl  * is meaningful only for writes. It's not possible to read after EOT (fails
75*9ee94b97SJan Kryl  * with EIO), but it's possible to write data after EOM. EOM returned by st
76*9ee94b97SJan Kryl  * driver on modern tape drives is just indication that the physical end of
77*9ee94b97SJan Kryl  * tape medium is nearing and that writer should write just the necessary
78*9ee94b97SJan Kryl  * minimum and stop writing. When physical end of tape is reached all writes
79*9ee94b97SJan Kryl  * return EIO. If EOM is crossed during read operation then st driver doesn't
80*9ee94b97SJan Kryl  * bother to report it to client and that's alright because reads don't care
81*9ee94b97SJan Kryl  * where medium physically ends but they care about meaningful data recorded on
82*9ee94b97SJan Kryl  * the tape and as long as there are such data reads should continue to work.
83*9ee94b97SJan Kryl  *
84*9ee94b97SJan Kryl  * When reading EOT is signalled by st driver by two empty consecutive reads
85*9ee94b97SJan Kryl  * (with FSF done between them).  When writing EOM is signalled by empty write
86*9ee94b97SJan Kryl  * (a write which writes zero bytes). Following writes succeed until physical
87*9ee94b97SJan Kryl  * end of tape is reached in which case EIO is returned.
88*9ee94b97SJan Kryl  */
89*9ee94b97SJan Kryl 
902654012fSReza Sabdar /*
912654012fSReza Sabdar  * ************************************************************************
922654012fSReza Sabdar  * NDMP V2 HANDLERS
932654012fSReza Sabdar  * ************************************************************************
942654012fSReza Sabdar  */
952654012fSReza Sabdar 
962654012fSReza Sabdar /*
972654012fSReza Sabdar  * ndmpd_tape_open_v2
982654012fSReza Sabdar  *
992654012fSReza Sabdar  * This handler opens the specified tape device.
1002654012fSReza Sabdar  *
1012654012fSReza Sabdar  * Parameters:
1022654012fSReza Sabdar  *   connection (input) - connection handle.
1032654012fSReza Sabdar  *   body       (input) - request message body.
1042654012fSReza Sabdar  *
1052654012fSReza Sabdar  * Returns:
1062654012fSReza Sabdar  *   void
1072654012fSReza Sabdar  */
1082654012fSReza Sabdar void
ndmpd_tape_open_v2(ndmp_connection_t * connection,void * body)1092654012fSReza Sabdar ndmpd_tape_open_v2(ndmp_connection_t *connection, void *body)
1102654012fSReza Sabdar {
1112654012fSReza Sabdar 	ndmp_tape_open_request_v2 *request = (ndmp_tape_open_request_v2 *) body;
1122654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
1132654012fSReza Sabdar 	char adptnm[SCSI_MAX_NAME];
1142654012fSReza Sabdar 	int mode;
1152654012fSReza Sabdar 	int sid, lun;
1162654012fSReza Sabdar 	int err;
1172654012fSReza Sabdar 	scsi_adapter_t *sa;
1182654012fSReza Sabdar 	int devid;
1192654012fSReza Sabdar 
1202654012fSReza Sabdar 	err = NDMP_NO_ERR;
1212654012fSReza Sabdar 
1222654012fSReza Sabdar 	if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) {
1232654012fSReza Sabdar 		NDMP_LOG(LOG_INFO,
1242654012fSReza Sabdar 		    "Connection already has a tape or scsi device open");
1252654012fSReza Sabdar 		err = NDMP_DEVICE_OPENED_ERR;
1262654012fSReza Sabdar 	} else if (request->mode != NDMP_TAPE_READ_MODE &&
1272654012fSReza Sabdar 	    request->mode != NDMP_TAPE_WRITE_MODE &&
1282654012fSReza Sabdar 	    request->mode != NDMP_TAPE_RAW1_MODE) {
1292654012fSReza Sabdar 		err = NDMP_ILLEGAL_ARGS_ERR;
1302654012fSReza Sabdar 	}
1312654012fSReza Sabdar 
1322654012fSReza Sabdar 	if ((sa = scsi_get_adapter(0)) != NULL) {
1332654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
1342654012fSReza Sabdar 		    "Adapter device opened: %s", request->device.name);
1352654012fSReza Sabdar 		(void) strlcpy(adptnm, request->device.name, SCSI_MAX_NAME-2);
1362654012fSReza Sabdar 		adptnm[SCSI_MAX_NAME-1] = '\0';
1372654012fSReza Sabdar 		sid = lun = -1;
1382654012fSReza Sabdar 	}
1392654012fSReza Sabdar 	/* try to get the scsi id etc.... */
1402654012fSReza Sabdar 	if (sa) {
1412654012fSReza Sabdar 		scsi_find_sid_lun(sa, request->device.name, &sid, &lun);
1422654012fSReza Sabdar 		if (ndmp_open_list_find(request->device.name, sid, lun) == 0 &&
1432654012fSReza Sabdar 		    (devid = tape_open(request->device.name,
1442654012fSReza Sabdar 		    O_RDWR | O_NDELAY)) < 0) {
1452654012fSReza Sabdar 			NDMP_LOG(LOG_ERR, "Failed to open device %s: %m.",
1462654012fSReza Sabdar 			    request->device.name);
1472654012fSReza Sabdar 			err = NDMP_NO_DEVICE_ERR;
1482654012fSReza Sabdar 		}
1492654012fSReza Sabdar 		else
1502654012fSReza Sabdar 			(void) close(devid);
1512654012fSReza Sabdar 	} else {
1522654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "%s: No such tape device.",
1532654012fSReza Sabdar 		    request->device.name);
1542654012fSReza Sabdar 		err = NDMP_NO_DEVICE_ERR;
1552654012fSReza Sabdar 	}
1562654012fSReza Sabdar 	if (err != NDMP_NO_ERR) {
1572654012fSReza Sabdar 		tape_open_send_reply(connection, err);
1582654012fSReza Sabdar 		return;
1592654012fSReza Sabdar 	}
1602654012fSReza Sabdar 
1612654012fSReza Sabdar 	switch (ndmp_open_list_add(connection, adptnm, sid, lun, devid)) {
1622654012fSReza Sabdar 	case 0:
1632654012fSReza Sabdar 		err = NDMP_NO_ERR;
1642654012fSReza Sabdar 		break;
1652654012fSReza Sabdar 	case EBUSY:
1662654012fSReza Sabdar 		err = NDMP_DEVICE_BUSY_ERR;
1672654012fSReza Sabdar 		break;
1682654012fSReza Sabdar 	case ENOMEM:
1692654012fSReza Sabdar 		err = NDMP_NO_MEM_ERR;
1702654012fSReza Sabdar 		break;
1712654012fSReza Sabdar 	default:
1722654012fSReza Sabdar 		err = NDMP_IO_ERR;
1732654012fSReza Sabdar 	}
1742654012fSReza Sabdar 	if (err != NDMP_NO_ERR) {
1752654012fSReza Sabdar 		tape_open_send_reply(connection, err);
1762654012fSReza Sabdar 		return;
1772654012fSReza Sabdar 	}
1782654012fSReza Sabdar 
1792654012fSReza Sabdar 	/*
1802654012fSReza Sabdar 	 * According to Connectathon 2001, the 0x7fffffff is a secret
1812654012fSReza Sabdar 	 * code between "Workstartion Solutions" and * net_app.
1822654012fSReza Sabdar 	 * If mode is set to this value, tape_open() won't fail if
1832654012fSReza Sabdar 	 * the tape device is not ready.
1842654012fSReza Sabdar 	 */
1852654012fSReza Sabdar 	if (request->mode != NDMP_TAPE_RAW1_MODE &&
1862654012fSReza Sabdar 	    !is_tape_unit_ready(adptnm, 0)) {
1872654012fSReza Sabdar 		(void) ndmp_open_list_del(adptnm, sid, lun);
1882654012fSReza Sabdar 		tape_open_send_reply(connection, NDMP_NO_TAPE_LOADED_ERR);
1892654012fSReza Sabdar 		return;
1902654012fSReza Sabdar 	}
1912654012fSReza Sabdar 
1922654012fSReza Sabdar 	mode = (request->mode == NDMP_TAPE_READ_MODE) ? O_RDONLY : O_RDWR;
1932654012fSReza Sabdar 	mode |= O_NDELAY;
1942654012fSReza Sabdar 	if ((session->ns_tape.td_fd = open(request->device.name, mode)) < 0) {
1952654012fSReza Sabdar 			NDMP_LOG(LOG_ERR, "Failed to open tape device %s: %m.",
1962654012fSReza Sabdar 			    request->device.name);
1972654012fSReza Sabdar 			switch (errno) {
1982654012fSReza Sabdar 			case EACCES:
1992654012fSReza Sabdar 				err = NDMP_WRITE_PROTECT_ERR;
2002654012fSReza Sabdar 				break;
2012654012fSReza Sabdar 			case ENXIO:
2022654012fSReza Sabdar 			case ENOENT:
2032654012fSReza Sabdar 				err = NDMP_NO_DEVICE_ERR;
2042654012fSReza Sabdar 				break;
2052654012fSReza Sabdar 			case EBUSY:
2062654012fSReza Sabdar 				err = NDMP_DEVICE_BUSY_ERR;
2072654012fSReza Sabdar 				break;
2082654012fSReza Sabdar 			default:
2092654012fSReza Sabdar 				err = NDMP_IO_ERR;
2102654012fSReza Sabdar 			}
2112654012fSReza Sabdar 
2122654012fSReza Sabdar 			(void) ndmp_open_list_del(adptnm, sid, lun);
2132654012fSReza Sabdar 			tape_open_send_reply(connection, err);
2142654012fSReza Sabdar 			return;
2152654012fSReza Sabdar 		}
2162654012fSReza Sabdar 
2172654012fSReza Sabdar 	session->ns_tape.td_mode = request->mode;
2182654012fSReza Sabdar 	session->ns_tape.td_sid = sid;
2192654012fSReza Sabdar 	session->ns_tape.td_lun = lun;
2202654012fSReza Sabdar 	(void) strlcpy(session->ns_tape.td_adapter_name, adptnm, SCSI_MAX_NAME);
2212654012fSReza Sabdar 	session->ns_tape.td_record_count = 0;
2222654012fSReza Sabdar 
2232654012fSReza Sabdar 	NDMP_LOG(LOG_DEBUG, "Tape is opened fd: %d", session->ns_tape.td_fd);
2242654012fSReza Sabdar 
2252654012fSReza Sabdar 	tape_open_send_reply(connection, NDMP_NO_ERR);
2262654012fSReza Sabdar }
2272654012fSReza Sabdar 
2282654012fSReza Sabdar 
2292654012fSReza Sabdar /*
2302654012fSReza Sabdar  * ndmpd_tape_close_v2
2312654012fSReza Sabdar  *
2322654012fSReza Sabdar  * This handler closes the currently open tape device.
2332654012fSReza Sabdar  *
2342654012fSReza Sabdar  * Parameters:
2352654012fSReza Sabdar  *   connection (input) - connection handle.
2362654012fSReza Sabdar  *   body       (input) - request message body.
2372654012fSReza Sabdar  *
2382654012fSReza Sabdar  * Returns:
2392654012fSReza Sabdar  *   void
2402654012fSReza Sabdar  */
2412654012fSReza Sabdar /*ARGSUSED*/
2422654012fSReza Sabdar void
ndmpd_tape_close_v2(ndmp_connection_t * connection,void * body)2432654012fSReza Sabdar ndmpd_tape_close_v2(ndmp_connection_t *connection, void *body)
2442654012fSReza Sabdar {
2452654012fSReza Sabdar 	ndmp_tape_close_reply reply;
2462654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
2472654012fSReza Sabdar 
2482654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
2492654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
2502654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
2512654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
2522654012fSReza Sabdar 		    "sending tape_close reply");
2532654012fSReza Sabdar 		return;
2542654012fSReza Sabdar 	}
2552654012fSReza Sabdar 	common_tape_close(connection);
2562654012fSReza Sabdar 
2572654012fSReza Sabdar }
2582654012fSReza Sabdar 
2592654012fSReza Sabdar /*
2602654012fSReza Sabdar  * ndmpd_tape_get_state_v2
2612654012fSReza Sabdar  *
2622654012fSReza Sabdar  * This handler handles the tape_get_state request.
2632654012fSReza Sabdar  * Status information for the currently open tape device is returned.
2642654012fSReza Sabdar  *
2652654012fSReza Sabdar  * Parameters:
2662654012fSReza Sabdar  *   connection (input) - connection handle.
2672654012fSReza Sabdar  *   body       (input) - request message body.
2682654012fSReza Sabdar  *
2692654012fSReza Sabdar  * Returns:
2702654012fSReza Sabdar  *   void
2712654012fSReza Sabdar  */
2722654012fSReza Sabdar /*ARGSUSED*/
2732654012fSReza Sabdar void
ndmpd_tape_get_state_v2(ndmp_connection_t * connection,void * body)2742654012fSReza Sabdar ndmpd_tape_get_state_v2(ndmp_connection_t *connection, void *body)
2752654012fSReza Sabdar 
2762654012fSReza Sabdar {
2772654012fSReza Sabdar 	ndmp_tape_get_state_reply_v2 reply;
2782654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
2792654012fSReza Sabdar 	struct mtget mtstatus;
2802654012fSReza Sabdar 	struct mtdrivetype_request dtpr;
2812654012fSReza Sabdar 	struct mtdrivetype dtp;
2822654012fSReza Sabdar 
2832654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
2842654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
2852654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
2862654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
2872654012fSReza Sabdar 		    "sending tape_get_state reply");
2882654012fSReza Sabdar 		return;
2892654012fSReza Sabdar 	}
2902654012fSReza Sabdar 
2912654012fSReza Sabdar 	if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) < 0) {
2922654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Failed to get status from tape: %m.");
2932654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGET) error: %m.");
2942654012fSReza Sabdar 		reply.error = NDMP_IO_ERR;
2952654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
2962654012fSReza Sabdar 		    "sending tape_get_state reply");
2972654012fSReza Sabdar 		return;
2982654012fSReza Sabdar 	}
2992654012fSReza Sabdar 
3002654012fSReza Sabdar 	dtpr.size = sizeof (struct mtdrivetype);
3012654012fSReza Sabdar 	dtpr.mtdtp = &dtp;
3022654012fSReza Sabdar 	if (ioctl(session->ns_tape.td_fd, MTIOCGETDRIVETYPE, &dtpr) == -1) {
3032654012fSReza Sabdar 		NDMP_LOG(LOG_ERR,
3042654012fSReza Sabdar 		    "Failed to get drive type information from tape: %m.");
3052654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGETDRIVETYPE) error: %m.");
3062654012fSReza Sabdar 		reply.error = NDMP_IO_ERR;
3072654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
3082654012fSReza Sabdar 		    "sending tape_get_state reply");
3092654012fSReza Sabdar 		return;
3102654012fSReza Sabdar 	}
3112654012fSReza Sabdar 
3122654012fSReza Sabdar 	reply.flags = 0;
3132654012fSReza Sabdar 
3142654012fSReza Sabdar 	reply.file_num = mtstatus.mt_fileno;
3152654012fSReza Sabdar 	reply.soft_errors = 0;
3162654012fSReza Sabdar 	reply.block_size = dtp.bsize;
3172654012fSReza Sabdar 	if (dtp.bsize == 0)
3182654012fSReza Sabdar 		reply.blockno = mtstatus.mt_blkno;
3192654012fSReza Sabdar 	else
3202654012fSReza Sabdar 		reply.blockno = mtstatus.mt_blkno *
3212654012fSReza Sabdar 		    (session->ns_mover.md_record_size / dtp.bsize);
3222654012fSReza Sabdar 
3232654012fSReza Sabdar 	reply.soft_errors = 0;
3242654012fSReza Sabdar 	reply.total_space = long_long_to_quad(0);	/* not supported */
3252654012fSReza Sabdar 	reply.space_remain = long_long_to_quad(0);	/* not supported */
3262654012fSReza Sabdar 
3272654012fSReza Sabdar 	NDMP_LOG(LOG_DEBUG,
3282654012fSReza Sabdar 	    "flags: 0x%x, file_num: %d, block_size: %d, blockno: %d",
3292654012fSReza Sabdar 	    reply.flags, reply.file_num, reply.block_size, reply.blockno);
3302654012fSReza Sabdar 
3312654012fSReza Sabdar 	reply.error = NDMP_NO_ERR;
3322654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply,
3332654012fSReza Sabdar 	    "sending tape_get_state reply");
3342654012fSReza Sabdar }
3352654012fSReza Sabdar 
3362654012fSReza Sabdar 
3372654012fSReza Sabdar /*
3382654012fSReza Sabdar  * ndmpd_tape_mtio_v2
3392654012fSReza Sabdar  *
3402654012fSReza Sabdar  * This handler handles tape_mtio requests.
3412654012fSReza Sabdar  *
3422654012fSReza Sabdar  * Parameters:
3432654012fSReza Sabdar  *   connection (input) - connection handle.
3442654012fSReza Sabdar  *   body       (input) - request message body.
3452654012fSReza Sabdar  *
3462654012fSReza Sabdar  * Returns:
3472654012fSReza Sabdar  *   void
3482654012fSReza Sabdar  */
3492654012fSReza Sabdar void
ndmpd_tape_mtio_v2(ndmp_connection_t * connection,void * body)3502654012fSReza Sabdar ndmpd_tape_mtio_v2(ndmp_connection_t *connection, void *body)
3512654012fSReza Sabdar {
3522654012fSReza Sabdar 	ndmp_tape_mtio_request *request = (ndmp_tape_mtio_request *) body;
3532654012fSReza Sabdar 	ndmp_tape_mtio_reply reply;
3542654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
3552654012fSReza Sabdar 
3562654012fSReza Sabdar 	struct mtop tapeop;
3572654012fSReza Sabdar 	struct mtget mtstatus;
3582654012fSReza Sabdar 	int retry = 0;
3592654012fSReza Sabdar 	int rc;
3602654012fSReza Sabdar 
3612654012fSReza Sabdar 	reply.resid_count = 0;
3622654012fSReza Sabdar 
3632654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
3642654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
3652654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
3662654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
3672654012fSReza Sabdar 		    "sending tape_mtio reply");
3682654012fSReza Sabdar 		return;
3692654012fSReza Sabdar 	}
3702654012fSReza Sabdar 
3712654012fSReza Sabdar 	reply.error = NDMP_NO_ERR;
3722654012fSReza Sabdar 	switch (request->tape_op) {
3732654012fSReza Sabdar 	case NDMP_MTIO_FSF:
3742654012fSReza Sabdar 		tapeop.mt_op = MTFSF;
3752654012fSReza Sabdar 		break;
3762654012fSReza Sabdar 	case NDMP_MTIO_BSF:
3772654012fSReza Sabdar 		tapeop.mt_op = MTBSF;
3782654012fSReza Sabdar 		break;
3792654012fSReza Sabdar 	case NDMP_MTIO_FSR:
3802654012fSReza Sabdar 		tapeop.mt_op = MTFSR;
3812654012fSReza Sabdar 		break;
3822654012fSReza Sabdar 	case NDMP_MTIO_BSR:
3832654012fSReza Sabdar 		tapeop.mt_op = MTBSR;
3842654012fSReza Sabdar 		break;
3852654012fSReza Sabdar 	case NDMP_MTIO_REW:
3862654012fSReza Sabdar 		tapeop.mt_op = MTREW;
3872654012fSReza Sabdar 		break;
3882654012fSReza Sabdar 	case NDMP_MTIO_EOF:
3892654012fSReza Sabdar 		if (session->ns_tape.td_mode == NDMP_TAPE_READ_MODE)
3902654012fSReza Sabdar 			reply.error = NDMP_PERMISSION_ERR;
3912654012fSReza Sabdar 		tapeop.mt_op = MTWEOF;
3922654012fSReza Sabdar 		break;
3932654012fSReza Sabdar 	case NDMP_MTIO_OFF:
3942654012fSReza Sabdar 		tapeop.mt_op = MTOFFL;
3952654012fSReza Sabdar 		break;
3962654012fSReza Sabdar 
3972654012fSReza Sabdar 	case NDMP_MTIO_TUR: /* test unit ready */
3982654012fSReza Sabdar 
3992654012fSReza Sabdar 		if (is_tape_unit_ready(session->ns_tape.td_adapter_name,
4002654012fSReza Sabdar 		    session->ns_tape.td_fd) == 0)
4012654012fSReza Sabdar 			/* tape not ready ? */
4022654012fSReza Sabdar 			reply.error = NDMP_NO_TAPE_LOADED_ERR;
4032654012fSReza Sabdar 		break;
4042654012fSReza Sabdar 
4052654012fSReza Sabdar 	default:
4062654012fSReza Sabdar 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
4072654012fSReza Sabdar 	}
4082654012fSReza Sabdar 
4092654012fSReza Sabdar 	if (reply.error == NDMP_NO_ERR && request->tape_op != NDMP_MTIO_TUR) {
4102654012fSReza Sabdar 		tapeop.mt_count = request->count;
4112654012fSReza Sabdar 
4122654012fSReza Sabdar 		do {
4132654012fSReza Sabdar 			NS_UPD(twait, trun);
414*9ee94b97SJan Kryl 			errno = 0;
4152654012fSReza Sabdar 			rc = ioctl(session->ns_tape.td_fd, MTIOCTOP, &tapeop);
4162654012fSReza Sabdar 			NS_UPD(trun, twait);
4172654012fSReza Sabdar 			NDMP_LOG(LOG_DEBUG,
4182654012fSReza Sabdar 			    "ioctl MTIO rc:%d, cmd:%d, retry:%d, error: %d",
4192654012fSReza Sabdar 			    rc, tapeop.mt_op, retry, errno);
4202654012fSReza Sabdar 		} while (rc < 0 && errno == EIO &&
4212654012fSReza Sabdar 		    retry++ < 5);
4222654012fSReza Sabdar 
4232654012fSReza Sabdar 		/*
4242654012fSReza Sabdar 		 * Ignore I/O errors since these usually are the result of
4252654012fSReza Sabdar 		 * attempting to position past the beginning or end of the tape.
4262654012fSReza Sabdar 		 * The residual count will be returned and can be used to
4272654012fSReza Sabdar 		 * determine that the call was not completely successful.
4282654012fSReza Sabdar 		 */
4292654012fSReza Sabdar 		if (rc < 0) {
4302654012fSReza Sabdar 			NDMP_LOG(LOG_ERR,
4312654012fSReza Sabdar 			    "Failed to send command to tape: %m.");
4322654012fSReza Sabdar 			NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCTOP) error: %m.");
4332654012fSReza Sabdar 
4342654012fSReza Sabdar 			/* MTWEOF doesnt have residual count */
4352654012fSReza Sabdar 			if (tapeop.mt_op == MTWEOF)
4362654012fSReza Sabdar 				reply.error = NDMP_IO_ERR;
4372654012fSReza Sabdar 			else
4382654012fSReza Sabdar 				reply.error = NDMP_NO_ERR;
4392654012fSReza Sabdar 			reply.resid_count = tapeop.mt_count;
4402654012fSReza Sabdar 			ndmp_send_reply(connection, (void *)&reply,
4412654012fSReza Sabdar 			    "sending tape_mtio reply");
4422654012fSReza Sabdar 			return;
4432654012fSReza Sabdar 		}
4442654012fSReza Sabdar 
4452654012fSReza Sabdar 		if (request->tape_op != NDMP_MTIO_REW &&
4462654012fSReza Sabdar 		    request->tape_op != NDMP_MTIO_OFF) {
4472654012fSReza Sabdar 			if (ioctl(session->ns_tape.td_fd, MTIOCGET,
4482654012fSReza Sabdar 			    &mtstatus) < 0) {
4492654012fSReza Sabdar 				NDMP_LOG(LOG_ERR,
4502654012fSReza Sabdar 				    "Failed to send command to tape: %m.");
4512654012fSReza Sabdar 				NDMP_LOG(LOG_DEBUG,
4522654012fSReza Sabdar 				    "ioctl(MTIOCGET) error: %m.");
4532654012fSReza Sabdar 				reply.error = NDMP_IO_ERR;
4542654012fSReza Sabdar 				ndmp_send_reply(connection, (void *)&reply,
4552654012fSReza Sabdar 				    "sending tape_mtio reply");
4562654012fSReza Sabdar 
4572654012fSReza Sabdar 				return;
4582654012fSReza Sabdar 			}
4592654012fSReza Sabdar 
4602654012fSReza Sabdar 			reply.resid_count = labs(mtstatus.mt_resid);
4612654012fSReza Sabdar 		}
4622654012fSReza Sabdar 	}
4632654012fSReza Sabdar 
4642654012fSReza Sabdar 	NDMP_LOG(LOG_DEBUG, "resid_count: %d",
4652654012fSReza Sabdar 	    reply.resid_count);
4662654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply, "sending tape_mtio reply");
4672654012fSReza Sabdar }
4682654012fSReza Sabdar 
4692654012fSReza Sabdar 
4702654012fSReza Sabdar /*
4712654012fSReza Sabdar  * ndmpd_tape_read_v2
4722654012fSReza Sabdar  *
4732654012fSReza Sabdar  * This handler handles tape_read requests.
4742654012fSReza Sabdar  * This interface is a non-buffered interface. Each read request
4752654012fSReza Sabdar  * maps directly to a read to the tape device. It is the responsibility
4762654012fSReza Sabdar  * of the NDMP client to issue read requests with a length that is at
4772654012fSReza Sabdar  * least as large as the record size used write the tape. The tape driver
4782654012fSReza Sabdar  * always reads a full record. Data is discarded if the read request is
4792654012fSReza Sabdar  * smaller than the record size.
4802654012fSReza Sabdar  * It is the responsibility of the NDMP client to ensure that the
4812654012fSReza Sabdar  * length is a multiple of the tape block size if the tape device
4822654012fSReza Sabdar  * is in fixed block mode.
4832654012fSReza Sabdar  *
4842654012fSReza Sabdar  * Parameters:
4852654012fSReza Sabdar  *   connection (input) - connection handle.
4862654012fSReza Sabdar  *   body       (input) - request message body.
4872654012fSReza Sabdar  *
4882654012fSReza Sabdar  * Returns:
4892654012fSReza Sabdar  *   void
4902654012fSReza Sabdar  */
4912654012fSReza Sabdar void
ndmpd_tape_read_v2(ndmp_connection_t * connection,void * body)4922654012fSReza Sabdar ndmpd_tape_read_v2(ndmp_connection_t *connection, void *body)
4932654012fSReza Sabdar {
4942654012fSReza Sabdar 	ndmp_tape_read_request *request = (ndmp_tape_read_request *) body;
4952654012fSReza Sabdar 	ndmp_tape_read_reply reply;
4962654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
4972654012fSReza Sabdar 	char *buf;
4982654012fSReza Sabdar 
4992654012fSReza Sabdar 	reply.data_in.data_in_len = 0;
5002654012fSReza Sabdar 
5012654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
5022654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
5032654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
5042654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
5052654012fSReza Sabdar 		    "sending tape_read reply");
5062654012fSReza Sabdar 		return;
5072654012fSReza Sabdar 	}
5082654012fSReza Sabdar 	if (request->count == 0) {
5092654012fSReza Sabdar 		reply.error = NDMP_NO_ERR;
5102654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
5112654012fSReza Sabdar 		    "sending tape_read reply");
5122654012fSReza Sabdar 		return;
5132654012fSReza Sabdar 	}
5142654012fSReza Sabdar 	if ((buf = ndmp_malloc(request->count)) == 0) {
5152654012fSReza Sabdar 		reply.error = NDMP_NO_MEM_ERR;
5162654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
5172654012fSReza Sabdar 		    "sending tape_read reply");
5182654012fSReza Sabdar 		return;
5192654012fSReza Sabdar 	}
5202654012fSReza Sabdar 
5212654012fSReza Sabdar 	unbuffered_read(session, buf, request->count, &reply);
5222654012fSReza Sabdar 
5232654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply, "sending tape_read reply");
5242654012fSReza Sabdar 	(void) free(buf);
5252654012fSReza Sabdar }
5262654012fSReza Sabdar 
5272654012fSReza Sabdar 
5282654012fSReza Sabdar /*
5292654012fSReza Sabdar  * ndmpd_tape_execute_cdb_v2
5302654012fSReza Sabdar  *
5312654012fSReza Sabdar  * This handler handles tape_execute_cdb requests.
5322654012fSReza Sabdar  *
5332654012fSReza Sabdar  * Parameters:
5342654012fSReza Sabdar  *   connection (input) - connection handle.
5352654012fSReza Sabdar  *   body       (input) - request message body.
5362654012fSReza Sabdar  *
5372654012fSReza Sabdar  * Returns:
5382654012fSReza Sabdar  *   void
5392654012fSReza Sabdar  */
5402654012fSReza Sabdar void
ndmpd_tape_execute_cdb_v2(ndmp_connection_t * connection,void * body)5412654012fSReza Sabdar ndmpd_tape_execute_cdb_v2(ndmp_connection_t *connection, void *body)
5422654012fSReza Sabdar {
5432654012fSReza Sabdar 	ndmp_tape_execute_cdb_request *request;
5442654012fSReza Sabdar 	ndmp_tape_execute_cdb_reply reply;
5452654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
5462654012fSReza Sabdar 
5472654012fSReza Sabdar 	request = (ndmp_tape_execute_cdb_request *) body;
5482654012fSReza Sabdar 
5492654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
5502654012fSReza Sabdar 		(void) memset((void *) &reply, 0, sizeof (reply));
5512654012fSReza Sabdar 
5522654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
5532654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
5542654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
5552654012fSReza Sabdar 		    "sending tape_execute_cdb reply");
5562654012fSReza Sabdar 	} else {
5572654012fSReza Sabdar 		ndmp_execute_cdb(session, session->ns_tape.td_adapter_name,
5582654012fSReza Sabdar 		    session->ns_tape.td_sid, session->ns_tape.td_lun,
5592654012fSReza Sabdar 		    (ndmp_execute_cdb_request *)request);
5602654012fSReza Sabdar 	}
5612654012fSReza Sabdar }
5622654012fSReza Sabdar 
5632654012fSReza Sabdar 
5642654012fSReza Sabdar /*
5652654012fSReza Sabdar  * ************************************************************************
5662654012fSReza Sabdar  * NDMP V3 HANDLERS
5672654012fSReza Sabdar  * ************************************************************************
5682654012fSReza Sabdar  */
5692654012fSReza Sabdar 
5702654012fSReza Sabdar /*
5712654012fSReza Sabdar  * ndmpd_tape_open_v3
5722654012fSReza Sabdar  *
5732654012fSReza Sabdar  * This handler opens the specified tape device.
5742654012fSReza Sabdar  *
5752654012fSReza Sabdar  * Parameters:
5762654012fSReza Sabdar  *   connection (input) - connection handle.
5772654012fSReza Sabdar  *   body       (input) - request message body.
5782654012fSReza Sabdar  *
5792654012fSReza Sabdar  * Returns:
5802654012fSReza Sabdar  *   void
5812654012fSReza Sabdar  */
5822654012fSReza Sabdar void
ndmpd_tape_open_v3(ndmp_connection_t * connection,void * body)5832654012fSReza Sabdar ndmpd_tape_open_v3(ndmp_connection_t *connection, void *body)
5842654012fSReza Sabdar {
5852654012fSReza Sabdar 	ndmp_tape_open_request_v3 *request = (ndmp_tape_open_request_v3 *)body;
5862654012fSReza Sabdar 
5872654012fSReza Sabdar 	common_tape_open(connection, request->device, request->mode);
5882654012fSReza Sabdar }
5892654012fSReza Sabdar 
5902654012fSReza Sabdar 
5912654012fSReza Sabdar /*
5922654012fSReza Sabdar  * ndmpd_tape_get_state_v3
5932654012fSReza Sabdar  *
5942654012fSReza Sabdar  * This handler handles the ndmp_tape_get_state_request.
5952654012fSReza Sabdar  * Status information for the currently open tape device is returned.
5962654012fSReza Sabdar  *
5972654012fSReza Sabdar  * Parameters:
5982654012fSReza Sabdar  *   connection (input) - connection handle.
5992654012fSReza Sabdar  *   body       (input) - request message body.
6002654012fSReza Sabdar  *
6012654012fSReza Sabdar  * Returns:
6022654012fSReza Sabdar  *   void
6032654012fSReza Sabdar  */
6042654012fSReza Sabdar /*ARGSUSED*/
6052654012fSReza Sabdar void
ndmpd_tape_get_state_v3(ndmp_connection_t * connection,void * body)6062654012fSReza Sabdar ndmpd_tape_get_state_v3(ndmp_connection_t *connection, void *body)
6072654012fSReza Sabdar {
6082654012fSReza Sabdar 	ndmp_tape_get_state_reply_v3 reply;
6092654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
6102654012fSReza Sabdar 	struct mtdrivetype_request dtpr;
6112654012fSReza Sabdar 	struct mtdrivetype dtp;
6122654012fSReza Sabdar 	struct mtget mtstatus;
6132654012fSReza Sabdar 
6142654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
6152654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
6162654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
6172654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
6182654012fSReza Sabdar 		    "sending tape_get_state reply");
6192654012fSReza Sabdar 		return;
6202654012fSReza Sabdar 	}
6212654012fSReza Sabdar 
6222654012fSReza Sabdar 	if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == -1) {
6232654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Failed to get status from tape: %m.");
6242654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGET) error: %m.");
6252654012fSReza Sabdar 
6262654012fSReza Sabdar 		reply.error = NDMP_IO_ERR;
6272654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
6282654012fSReza Sabdar 		    "sending tape_get_state reply");
6292654012fSReza Sabdar 		return;
6302654012fSReza Sabdar 	}
6312654012fSReza Sabdar 
6322654012fSReza Sabdar 	dtpr.size = sizeof (struct mtdrivetype);
6332654012fSReza Sabdar 	dtpr.mtdtp = &dtp;
6342654012fSReza Sabdar 	if (ioctl(session->ns_tape.td_fd, MTIOCGETDRIVETYPE, &dtpr) == -1) {
6352654012fSReza Sabdar 		NDMP_LOG(LOG_ERR,
6362654012fSReza Sabdar 		    "Failed to get drive type information from tape: %m.");
6372654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGETDRIVETYPE) error: %m.");
6382654012fSReza Sabdar 
6392654012fSReza Sabdar 		reply.error = NDMP_IO_ERR;
6402654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
6412654012fSReza Sabdar 		    "sending tape_get_state reply");
6422654012fSReza Sabdar 		return;
6432654012fSReza Sabdar 	}
6442654012fSReza Sabdar 
6452654012fSReza Sabdar 	reply.flags = 0;
6462654012fSReza Sabdar 
6472654012fSReza Sabdar 	reply.file_num = mtstatus.mt_fileno;
6482654012fSReza Sabdar 	reply.soft_errors = 0;
6492654012fSReza Sabdar 	reply.block_size = dtp.bsize;
6502654012fSReza Sabdar 	if (dtp.bsize == 0)
6512654012fSReza Sabdar 		reply.blockno = mtstatus.mt_blkno;
6522654012fSReza Sabdar 	else
6532654012fSReza Sabdar 		reply.blockno = mtstatus.mt_blkno *
6542654012fSReza Sabdar 		    (session->ns_mover.md_record_size / dtp.bsize);
6552654012fSReza Sabdar 	reply.total_space = long_long_to_quad(0); /* not supported */
6562654012fSReza Sabdar 	reply.space_remain = long_long_to_quad(0); /* not supported */
6572654012fSReza Sabdar 	reply.partition = 0; /* not supported */
6582654012fSReza Sabdar 
6592654012fSReza Sabdar 	reply.soft_errors = 0;
6602654012fSReza Sabdar 	reply.total_space = long_long_to_quad(0LL);
6612654012fSReza Sabdar 	reply.space_remain = long_long_to_quad(0LL);
6622654012fSReza Sabdar 
6632654012fSReza Sabdar 	reply.invalid = NDMP_TAPE_STATE_SOFT_ERRORS_INVALID |
6642654012fSReza Sabdar 	    NDMP_TAPE_STATE_TOTAL_SPACE_INVALID |
6652654012fSReza Sabdar 	    NDMP_TAPE_STATE_SPACE_REMAIN_INVALID |
6662654012fSReza Sabdar 	    NDMP_TAPE_STATE_PARTITION_INVALID;
6672654012fSReza Sabdar 
6682654012fSReza Sabdar 
6692654012fSReza Sabdar 	NDMP_LOG(LOG_DEBUG, "f 0x%x, fnum %d, bsize %d, bno: %d",
6702654012fSReza Sabdar 	    reply.flags, reply.file_num, reply.block_size, reply.blockno);
6712654012fSReza Sabdar 
6722654012fSReza Sabdar 	reply.error = NDMP_NO_ERR;
6732654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply,
6742654012fSReza Sabdar 	    "sending tape_get_state reply");
6752654012fSReza Sabdar }
6762654012fSReza Sabdar 
677*9ee94b97SJan Kryl /*
678*9ee94b97SJan Kryl  * tape_is_at_bot
679*9ee94b97SJan Kryl  *
680*9ee94b97SJan Kryl  * Returns 1 if tape is at BOT, 0 on error or not at BOT.
681*9ee94b97SJan Kryl  *
682*9ee94b97SJan Kryl  */
683*9ee94b97SJan Kryl int
tape_is_at_bot(ndmpd_session_t * session)684*9ee94b97SJan Kryl tape_is_at_bot(ndmpd_session_t *session)
685*9ee94b97SJan Kryl {
686*9ee94b97SJan Kryl 	struct mtget mtstatus;
687*9ee94b97SJan Kryl 
688*9ee94b97SJan Kryl 	if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == 0 &&
689*9ee94b97SJan Kryl 	    mtstatus.mt_fileno == 0 && mtstatus.mt_blkno == 0)
690*9ee94b97SJan Kryl 		return (1);
691*9ee94b97SJan Kryl 
692*9ee94b97SJan Kryl 	return (0);
693*9ee94b97SJan Kryl }
694*9ee94b97SJan Kryl 
695*9ee94b97SJan Kryl /*
696*9ee94b97SJan Kryl  * If we are at the beginning of a file (block # is zero) and read returns
697*9ee94b97SJan Kryl  * zero bytes then this has to be end of recorded data on the tape. Repeated
698*9ee94b97SJan Kryl  * reads at EOT return EIO. In both cases (zero read and EIO read) this
699*9ee94b97SJan Kryl  * function should be used to test if we are at EOT.
700*9ee94b97SJan Kryl  *
701*9ee94b97SJan Kryl  * Returns 1 if tape is at BOF, 0 on error or not at BOF.
702*9ee94b97SJan Kryl  */
703*9ee94b97SJan Kryl int
tape_is_at_bof(ndmpd_session_t * session)704*9ee94b97SJan Kryl tape_is_at_bof(ndmpd_session_t *session)
705*9ee94b97SJan Kryl {
706*9ee94b97SJan Kryl 	struct mtget mtstatus;
707*9ee94b97SJan Kryl 
708*9ee94b97SJan Kryl 	if ((ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == 0) &&
709*9ee94b97SJan Kryl 	    (mtstatus.mt_fileno > 0) && (mtstatus.mt_blkno == 0))
710*9ee94b97SJan Kryl 		return (1);
711*9ee94b97SJan Kryl 
712*9ee94b97SJan Kryl 	return (0);
713*9ee94b97SJan Kryl }
714*9ee94b97SJan Kryl 
715*9ee94b97SJan Kryl /*
716*9ee94b97SJan Kryl  * Skips forward over a file mark and then back before the file mark. Why is
717*9ee94b97SJan Kryl  * this needed? There are two reasons for it:
718*9ee94b97SJan Kryl  *
719*9ee94b97SJan Kryl  * 1) Because NDMPv4 spec requires that when EOF is encountered, the tape
720*9ee94b97SJan Kryl  * position should remain on BOT side of the file mark. When st driver reaches
721*9ee94b97SJan Kryl  * end of file get-position mtioctl reports position before file mark, however
722*9ee94b97SJan Kryl  * the file mark has already been read and the real position is thus after the
723*9ee94b97SJan Kryl  * file mark (real position as reported for example by uscsi commands). Thus we
724*9ee94b97SJan Kryl  * need to do FSF, which does nothing but only updates file & block counter in
725*9ee94b97SJan Kryl  * st driver and then BSF, which sets the position before the file mark. Thus
726*9ee94b97SJan Kryl  * current position as reported by scsi and mtioctl will be in sync.
727*9ee94b97SJan Kryl  *
728*9ee94b97SJan Kryl  * 2) st driver returns EIO for repeated reads at EOF while according to NDMP
729*9ee94b97SJan Kryl  * spec we should continue to return zero bytes until FSF is done. By skipping
730*9ee94b97SJan Kryl  * forward and backward, st driver will return zero bytes for the next read
731*9ee94b97SJan Kryl  * again and we don't need to specifically handle this case.
732*9ee94b97SJan Kryl  */
733*9ee94b97SJan Kryl void
fm_dance(ndmpd_session_t * session)734*9ee94b97SJan Kryl fm_dance(ndmpd_session_t *session)
735*9ee94b97SJan Kryl {
736*9ee94b97SJan Kryl 	(void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
737*9ee94b97SJan Kryl 	(void) ndmp_mtioctl(session->ns_tape.td_fd, MTBSF, 1);
738*9ee94b97SJan Kryl }
7392654012fSReza Sabdar 
7402654012fSReza Sabdar /*
7412654012fSReza Sabdar  * ndmpd_tape_write_v3
7422654012fSReza Sabdar  *
743*9ee94b97SJan Kryl  * This handler handles tape_write requests.  This interface is a non-buffered
744*9ee94b97SJan Kryl  * interface. Each write request maps directly to a write to the tape device.
745*9ee94b97SJan Kryl  * It is the responsibility of the NDMP client to pad the data to the desired
746*9ee94b97SJan Kryl  * record size.  It is the responsibility of the NDMP client to ensure that the
747*9ee94b97SJan Kryl  * length is a multiple of the tape block size if the tape device is in fixed
748*9ee94b97SJan Kryl  * block mode.
749*9ee94b97SJan Kryl  *
750*9ee94b97SJan Kryl  * A logical end of tape will return number of bytes written less than
751*9ee94b97SJan Kryl  * requested, and one more request to write will give 0 and NDMP_EOM_ERR,
752*9ee94b97SJan Kryl  * followed by NDMP_NO_ERR until NDMP_IO_ERR when physical end of tape is
753*9ee94b97SJan Kryl  * reached.
7542654012fSReza Sabdar  *
7552654012fSReza Sabdar  * Parameters:
7562654012fSReza Sabdar  *   connection (input) - connection handle.
7572654012fSReza Sabdar  *   body       (input) - request message body.
7582654012fSReza Sabdar  */
ndmpd_tape_write_v3(ndmp_connection_t * connection,void * body)759*9ee94b97SJan Kryl void ndmpd_tape_write_v3(ndmp_connection_t *connection, void *body) {
760*9ee94b97SJan Kryl 	ndmp_tape_write_request *request = (ndmp_tape_write_request *)body;
761*9ee94b97SJan Kryl 	ndmp_tape_write_reply reply; ndmpd_session_t *session =
762*9ee94b97SJan Kryl 		ndmp_get_client_data(connection); ssize_t n;
7632654012fSReza Sabdar 
7642654012fSReza Sabdar 	reply.count = 0;
7652654012fSReza Sabdar 
7662654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
7672654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
7682654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
7692654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
7702654012fSReza Sabdar 		    "sending tape_write reply");
7712654012fSReza Sabdar 		return;
7722654012fSReza Sabdar 	}
7732654012fSReza Sabdar 	if (session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
7742654012fSReza Sabdar 		NDMP_LOG(LOG_INFO, "Tape device opened in read-only mode");
7752654012fSReza Sabdar 		reply.error = NDMP_PERMISSION_ERR;
7762654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
7772654012fSReza Sabdar 		    "sending tape_write reply");
7782654012fSReza Sabdar 		return;
7792654012fSReza Sabdar 	}
7802654012fSReza Sabdar 	if (request->data_out.data_out_len == 0) {
7812654012fSReza Sabdar 		reply.error = NDMP_NO_ERR;
7822654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
7832654012fSReza Sabdar 		    "sending tape_write reply");
7842654012fSReza Sabdar 		return;
7852654012fSReza Sabdar 	}
7862654012fSReza Sabdar 
7872654012fSReza Sabdar 	/*
7882654012fSReza Sabdar 	 * V4 suggests that this should not be accepted
7892654012fSReza Sabdar 	 * when mover is in listen or active state
7902654012fSReza Sabdar 	 */
7912654012fSReza Sabdar 	if (session->ns_protocol_version == NDMPV4 &&
7922654012fSReza Sabdar 	    (session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
7932654012fSReza Sabdar 	    session->ns_mover.md_state == NDMP_MOVER_STATE_ACTIVE)) {
7942654012fSReza Sabdar 
7952654012fSReza Sabdar 		reply.error = NDMP_DEVICE_BUSY_ERR;
7962654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
7972654012fSReza Sabdar 		    "sending tape_write reply");
7982654012fSReza Sabdar 		return;
7992654012fSReza Sabdar 	}
8002654012fSReza Sabdar 
8012654012fSReza Sabdar 	n = write(session->ns_tape.td_fd, request->data_out.data_out_val,
8022654012fSReza Sabdar 	    request->data_out.data_out_len);
8032654012fSReza Sabdar 
804*9ee94b97SJan Kryl 	if (n < 0) {
8052654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape write error: %m.");
8062654012fSReza Sabdar 		reply.error = NDMP_IO_ERR;
807*9ee94b97SJan Kryl 	} else if (n == 0) {
808*9ee94b97SJan Kryl 		NDMP_LOG(LOG_INFO, "EOM detected");
809*9ee94b97SJan Kryl 		reply.error = NDMP_EOM_ERR;
8102654012fSReza Sabdar 	} else {
811*9ee94b97SJan Kryl 		NS_ADD(wtape, n);
8122654012fSReza Sabdar 		reply.count = n;
8132654012fSReza Sabdar 		reply.error = NDMP_NO_ERR;
814*9ee94b97SJan Kryl 
815*9ee94b97SJan Kryl 		if (n < request->data_out.data_out_len)
816*9ee94b97SJan Kryl 			NDMP_LOG(LOG_DEBUG,
817*9ee94b97SJan Kryl 				"EOM is coming (partial write of %d bytes)", n);
8182654012fSReza Sabdar 	}
8192654012fSReza Sabdar 
8202654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply,
8212654012fSReza Sabdar 	    "sending tape_write reply");
8222654012fSReza Sabdar }
8232654012fSReza Sabdar 
8242654012fSReza Sabdar /*
8252654012fSReza Sabdar  * ndmpd_tape_read_v3
8262654012fSReza Sabdar  *
827*9ee94b97SJan Kryl  * This handler handles tape_read requests.  This interface is a non-buffered
828*9ee94b97SJan Kryl  * interface. Each read request maps directly to a read to the tape device. It
829*9ee94b97SJan Kryl  * is the responsibility of the NDMP client to issue read requests with a
830*9ee94b97SJan Kryl  * length that is at least as large as the record size used write the tape. The
831*9ee94b97SJan Kryl  * tape driver always reads a full record. Data is discarded if the read
832*9ee94b97SJan Kryl  * request is smaller than the record size.  It is the responsibility of the
833*9ee94b97SJan Kryl  * NDMP client to ensure that the length is a multiple of the tape block size
834*9ee94b97SJan Kryl  * if the tape device is in fixed block mode.
835*9ee94b97SJan Kryl  *
836*9ee94b97SJan Kryl  * A logical end of tape will return less bytes than requested, and one more
837*9ee94b97SJan Kryl  * request to read will give 0 and NDMP_EOM_ERR.  All subsequent reads will
838*9ee94b97SJan Kryl  * return NDMP_EOM_ERR until the tape is repositioned.
8392654012fSReza Sabdar  *
8402654012fSReza Sabdar  * Parameters:
8412654012fSReza Sabdar  *   connection (input) - connection handle.
8422654012fSReza Sabdar  *   body       (input) - request message body.
8432654012fSReza Sabdar  */
8442654012fSReza Sabdar void
ndmpd_tape_read_v3(ndmp_connection_t * connection,void * body)8452654012fSReza Sabdar ndmpd_tape_read_v3(ndmp_connection_t *connection, void *body)
8462654012fSReza Sabdar {
8472654012fSReza Sabdar 	ndmp_tape_read_request *request = (ndmp_tape_read_request *) body;
8482654012fSReza Sabdar 	ndmp_tape_read_reply reply;
8492654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
8502654012fSReza Sabdar 	char *buf;
851*9ee94b97SJan Kryl 	int n;
8522654012fSReza Sabdar 
8532654012fSReza Sabdar 	reply.data_in.data_in_len = 0;
8542654012fSReza Sabdar 
8552654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
8562654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
8572654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
8582654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
8592654012fSReza Sabdar 		    "sending tape_read reply");
8602654012fSReza Sabdar 		return;
8612654012fSReza Sabdar 	}
8622654012fSReza Sabdar 	if (request->count == 0) {
8632654012fSReza Sabdar 		reply.error = NDMP_NO_ERR;
8642654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
8652654012fSReza Sabdar 		    "sending tape_read reply");
8662654012fSReza Sabdar 		return;
8672654012fSReza Sabdar 	}
8682654012fSReza Sabdar 
8692654012fSReza Sabdar 	/*
8702654012fSReza Sabdar 	 * V4 suggests that this should not be accepted
8712654012fSReza Sabdar 	 * when mover is in listen or active state
8722654012fSReza Sabdar 	 */
8732654012fSReza Sabdar 	if (session->ns_protocol_version == NDMPV4 &&
8742654012fSReza Sabdar 	    (session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
8752654012fSReza Sabdar 	    session->ns_mover.md_state == NDMP_MOVER_STATE_ACTIVE)) {
8762654012fSReza Sabdar 
8772654012fSReza Sabdar 		reply.error = NDMP_DEVICE_BUSY_ERR;
8782654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
8792654012fSReza Sabdar 		    "sending tape_read reply");
8802654012fSReza Sabdar 		return;
8812654012fSReza Sabdar 	}
8822654012fSReza Sabdar 
8832654012fSReza Sabdar 	if ((buf = ndmp_malloc(request->count)) == NULL) {
8842654012fSReza Sabdar 		reply.error = NDMP_NO_MEM_ERR;
8852654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
8862654012fSReza Sabdar 		    "sending tape_read reply");
8872654012fSReza Sabdar 		return;
8882654012fSReza Sabdar 	}
8892654012fSReza Sabdar 
8902654012fSReza Sabdar 	n = read(session->ns_tape.td_fd, buf, request->count);
8912654012fSReza Sabdar 	if (n < 0) {
8922654012fSReza Sabdar 		/*
8932654012fSReza Sabdar 		 * This fix is for Symantec during importing
8942654012fSReza Sabdar 		 * of spanned data between the tapes.
8952654012fSReza Sabdar 		 */
8962654012fSReza Sabdar 		if (errno == ENOSPC) {
8972654012fSReza Sabdar 			reply.error = NDMP_EOF_ERR;
898*9ee94b97SJan Kryl 		}
899*9ee94b97SJan Kryl 		/*
900*9ee94b97SJan Kryl 		 * If at beginning of file and read fails with EIO, then it's
901*9ee94b97SJan Kryl 		 * repeated attempt to read at EOT.
902*9ee94b97SJan Kryl 		 */
903*9ee94b97SJan Kryl 		else if (errno == EIO && tape_is_at_bof(session)) {
904*9ee94b97SJan Kryl 			NDMP_LOG(LOG_DEBUG, "Repeated read at EOT");
905*9ee94b97SJan Kryl 			reply.error = NDMP_EOM_ERR;
906*9ee94b97SJan Kryl 		}
907*9ee94b97SJan Kryl 		/*
908*9ee94b97SJan Kryl 		 * According to NDMPv4 spec preferred error code when
909*9ee94b97SJan Kryl 		 * trying to read from blank tape is NDMP_EOM_ERR.
910*9ee94b97SJan Kryl 		 */
911*9ee94b97SJan Kryl 		else if (errno == EIO && tape_is_at_bot(session)) {
912*9ee94b97SJan Kryl 			NDMP_LOG(LOG_ERR, "Blank tape detected, returning EOM");
913*9ee94b97SJan Kryl 			reply.error = NDMP_EOM_ERR;
9142654012fSReza Sabdar 		} else {
9152654012fSReza Sabdar 			NDMP_LOG(LOG_ERR, "Tape read error: %m.");
9162654012fSReza Sabdar 			reply.error = NDMP_IO_ERR;
9172654012fSReza Sabdar 		}
9182654012fSReza Sabdar 	} else if (n == 0) {
919*9ee94b97SJan Kryl 		if (tape_is_at_bof(session)) {
920*9ee94b97SJan Kryl 			NDMP_LOG(LOG_DEBUG, "EOT detected");
9212654012fSReza Sabdar 			reply.error = NDMP_EOM_ERR;
9222654012fSReza Sabdar 		} else {
923*9ee94b97SJan Kryl 			/* reposition the tape to BOT side of FM */
924*9ee94b97SJan Kryl 			fm_dance(session);
925*9ee94b97SJan Kryl 			NDMP_LOG(LOG_DEBUG, "EOF detected");
9262654012fSReza Sabdar 			reply.error = NDMP_EOF_ERR;
9272654012fSReza Sabdar 		}
9282654012fSReza Sabdar 	} else {
929*9ee94b97SJan Kryl 		session->ns_tape.td_pos += n;
930*9ee94b97SJan Kryl 		reply.data_in.data_in_len = n;
931*9ee94b97SJan Kryl 		reply.data_in.data_in_val = buf;
932*9ee94b97SJan Kryl 		reply.error = NDMP_NO_ERR;
9332654012fSReza Sabdar 		NS_ADD(rtape, n);
9342654012fSReza Sabdar 	}
9352654012fSReza Sabdar 
9362654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply, "sending tape_read reply");
9372654012fSReza Sabdar 	free(buf);
9382654012fSReza Sabdar }
9392654012fSReza Sabdar 
9402654012fSReza Sabdar 
9412654012fSReza Sabdar /*
9422654012fSReza Sabdar  * ************************************************************************
9432654012fSReza Sabdar  * NDMP V4 HANDLERS
9442654012fSReza Sabdar  * ************************************************************************
9452654012fSReza Sabdar  */
9462654012fSReza Sabdar 
9472654012fSReza Sabdar /*
9482654012fSReza Sabdar  * ndmpd_tape_get_state_v4
9492654012fSReza Sabdar  *
9502654012fSReza Sabdar  * This handler handles the ndmp_tape_get_state_request.
9512654012fSReza Sabdar  * Status information for the currently open tape device is returned.
9522654012fSReza Sabdar  *
9532654012fSReza Sabdar  * Parameters:
9542654012fSReza Sabdar  *   connection (input) - connection handle.
9552654012fSReza Sabdar  *   body       (input) - request message body.
9562654012fSReza Sabdar  *
9572654012fSReza Sabdar  * Returns:
9582654012fSReza Sabdar  *   void
9592654012fSReza Sabdar  */
9602654012fSReza Sabdar /*ARGSUSED*/
9612654012fSReza Sabdar void
ndmpd_tape_get_state_v4(ndmp_connection_t * connection,void * body)9622654012fSReza Sabdar ndmpd_tape_get_state_v4(ndmp_connection_t *connection, void *body)
9632654012fSReza Sabdar {
9642654012fSReza Sabdar 	ndmp_tape_get_state_reply_v4 reply;
9652654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
9662654012fSReza Sabdar 	struct mtget mtstatus;
9672654012fSReza Sabdar 	struct mtdrivetype_request dtpr;
9682654012fSReza Sabdar 	struct mtdrivetype dtp;
9692654012fSReza Sabdar 
9702654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
9712654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
9722654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
9732654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
9742654012fSReza Sabdar 		    "sending tape_get_state reply");
9752654012fSReza Sabdar 		return;
9762654012fSReza Sabdar 	}
9772654012fSReza Sabdar 
9782654012fSReza Sabdar 	/*
9792654012fSReza Sabdar 	 * Need code to detect NDMP_TAPE_STATE_NOREWIND
9802654012fSReza Sabdar 	 */
9812654012fSReza Sabdar 
9822654012fSReza Sabdar 	if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == -1) {
9832654012fSReza Sabdar 		NDMP_LOG(LOG_ERR,
9842654012fSReza Sabdar 		    "Failed to get status information from tape: %m.");
9852654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGET) error: %m.");
9862654012fSReza Sabdar 
9872654012fSReza Sabdar 		reply.error = NDMP_IO_ERR;
9882654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
9892654012fSReza Sabdar 		    "sending tape_get_state reply");
9902654012fSReza Sabdar 		return;
9912654012fSReza Sabdar 	}
9922654012fSReza Sabdar 
9932654012fSReza Sabdar 	dtpr.size = sizeof (struct mtdrivetype);
9942654012fSReza Sabdar 	dtpr.mtdtp = &dtp;
9952654012fSReza Sabdar 	if (ioctl(session->ns_tape.td_fd, MTIOCGETDRIVETYPE, &dtpr) == -1) {
9962654012fSReza Sabdar 		NDMP_LOG(LOG_ERR,
9972654012fSReza Sabdar 		    "Failed to get drive type information from tape: %m.");
9982654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGETDRIVETYPE) error: %m.");
9992654012fSReza Sabdar 
10002654012fSReza Sabdar 		reply.error = NDMP_IO_ERR;
10012654012fSReza Sabdar 		ndmp_send_reply(connection, (void *)&reply,
10022654012fSReza Sabdar 		    "sending tape_get_state reply");
10032654012fSReza Sabdar 		return;
10042654012fSReza Sabdar 	}
10052654012fSReza Sabdar 
100686c48bbfSReza Sabdar 	reply.flags = NDMP_TAPE_NOREWIND;
10072654012fSReza Sabdar 
10082654012fSReza Sabdar 	reply.file_num = mtstatus.mt_fileno;
10092654012fSReza Sabdar 	reply.soft_errors = 0;
10102654012fSReza Sabdar 	reply.block_size = dtp.bsize;
10112654012fSReza Sabdar 
10122654012fSReza Sabdar 	if (dtp.bsize == 0)
10132654012fSReza Sabdar 		reply.blockno = mtstatus.mt_blkno;
10142654012fSReza Sabdar 	else
1015*9ee94b97SJan Kryl 		reply.blockno = mtstatus.mt_blkno /
10162654012fSReza Sabdar 		    (session->ns_mover.md_record_size / dtp.bsize);
10172654012fSReza Sabdar 
1018*9ee94b97SJan Kryl 	reply.total_space = long_long_to_quad(0LL); /* not supported */
1019*9ee94b97SJan Kryl 	reply.space_remain = long_long_to_quad(0LL); /* not supported */
10202654012fSReza Sabdar 	reply.soft_errors = 0;
10212654012fSReza Sabdar 	reply.unsupported = NDMP_TAPE_STATE_SOFT_ERRORS_INVALID |
10222654012fSReza Sabdar 	    NDMP_TAPE_STATE_TOTAL_SPACE_INVALID |
10232654012fSReza Sabdar 	    NDMP_TAPE_STATE_SPACE_REMAIN_INVALID |
10242654012fSReza Sabdar 	    NDMP_TAPE_STATE_PARTITION_INVALID;
10252654012fSReza Sabdar 
10262654012fSReza Sabdar 	NDMP_LOG(LOG_DEBUG, "f 0x%x, fnum %d, bsize %d, bno: %d",
10272654012fSReza Sabdar 	    reply.flags, reply.file_num, reply.block_size, reply.blockno);
10282654012fSReza Sabdar 
10292654012fSReza Sabdar 	reply.error = NDMP_NO_ERR;
10302654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply,
10312654012fSReza Sabdar 	    "sending tape_get_state reply");
10322654012fSReza Sabdar }
10332654012fSReza Sabdar /*
10342654012fSReza Sabdar  * ndmpd_tape_close_v4
10352654012fSReza Sabdar  *
10362654012fSReza Sabdar  * This handler (v4) closes the currently open tape device.
10372654012fSReza Sabdar  *
10382654012fSReza Sabdar  * Parameters:
10392654012fSReza Sabdar  *   connection (input) - connection handle.
10402654012fSReza Sabdar  *   body       (input) - request message body.
10412654012fSReza Sabdar  *
10422654012fSReza Sabdar  * Returns:
10432654012fSReza Sabdar  *   void
10442654012fSReza Sabdar  */
10452654012fSReza Sabdar /*ARGSUSED*/
10462654012fSReza Sabdar void
ndmpd_tape_close_v4(ndmp_connection_t * connection,void * body)10472654012fSReza Sabdar ndmpd_tape_close_v4(ndmp_connection_t *connection, void *body)
10482654012fSReza Sabdar {
10492654012fSReza Sabdar 	ndmp_tape_close_reply reply;
10502654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
10512654012fSReza Sabdar 
10522654012fSReza Sabdar 	if (session->ns_tape.td_fd == -1) {
10532654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
10542654012fSReza Sabdar 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
10552654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
10562654012fSReza Sabdar 		    "sending tape_close reply");
10572654012fSReza Sabdar 		return;
10582654012fSReza Sabdar 	}
10592654012fSReza Sabdar 
10602654012fSReza Sabdar 	/*
10612654012fSReza Sabdar 	 * V4 suggests that this should not be accepted
10622654012fSReza Sabdar 	 * when mover is in listen or active state
10632654012fSReza Sabdar 	 */
10642654012fSReza Sabdar 	if (session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
10652654012fSReza Sabdar 	    session->ns_mover.md_state == NDMP_MOVER_STATE_ACTIVE) {
10662654012fSReza Sabdar 
10672654012fSReza Sabdar 		reply.error = NDMP_DEVICE_BUSY_ERR;
10682654012fSReza Sabdar 		ndmp_send_reply(connection, (void *) &reply,
10692654012fSReza Sabdar 		    "sending tape_close reply");
10702654012fSReza Sabdar 		return;
10712654012fSReza Sabdar 	}
10722654012fSReza Sabdar 
10732654012fSReza Sabdar 	common_tape_close(connection);
10742654012fSReza Sabdar }
10752654012fSReza Sabdar 
10762654012fSReza Sabdar 
10772654012fSReza Sabdar /*
10782654012fSReza Sabdar  * ************************************************************************
10792654012fSReza Sabdar  * LOCALS
10802654012fSReza Sabdar  * ************************************************************************
10812654012fSReza Sabdar  */
10822654012fSReza Sabdar /*
10832654012fSReza Sabdar  * tape_open_send_reply
10842654012fSReza Sabdar  *
10852654012fSReza Sabdar  * Send a reply to the tape open message
10862654012fSReza Sabdar  *
10872654012fSReza Sabdar  * Parameters:
10882654012fSReza Sabdar  *   connection (input) - connection handle.
10892654012fSReza Sabdar  *   err (input) - NDMP error
10902654012fSReza Sabdar  *
10912654012fSReza Sabdar  * Returns:
10922654012fSReza Sabdar  *   void
10932654012fSReza Sabdar  */
10942654012fSReza Sabdar static void
tape_open_send_reply(ndmp_connection_t * connection,int err)10952654012fSReza Sabdar tape_open_send_reply(ndmp_connection_t *connection, int err)
10962654012fSReza Sabdar {
10972654012fSReza Sabdar 	ndmp_tape_open_reply reply;
10982654012fSReza Sabdar 
10992654012fSReza Sabdar 	reply.error = err;
11002654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply, "sending tape_open reply");
11012654012fSReza Sabdar }
11022654012fSReza Sabdar 
11032654012fSReza Sabdar /*
11042654012fSReza Sabdar  * unbuffered_read
11052654012fSReza Sabdar  *
11062654012fSReza Sabdar  * Perform tape read without read-ahead
11072654012fSReza Sabdar  *
11082654012fSReza Sabdar  * Parameters:
11092654012fSReza Sabdar  *   session (input) - session handle
11102654012fSReza Sabdar  *   bp (output) - read buffer
11112654012fSReza Sabdar  *   wanted (input) - number of bytes wanted
11122654012fSReza Sabdar  *   reply (output) - tape read reply message
11132654012fSReza Sabdar  *
11142654012fSReza Sabdar  * Returns:
11152654012fSReza Sabdar  *   void
11162654012fSReza Sabdar  */
11172654012fSReza Sabdar static void
unbuffered_read(ndmpd_session_t * session,char * buf,long wanted,ndmp_tape_read_reply * reply)11182654012fSReza Sabdar unbuffered_read(ndmpd_session_t *session, char *buf, long wanted,
11192654012fSReza Sabdar     ndmp_tape_read_reply *reply)
11202654012fSReza Sabdar {
11212654012fSReza Sabdar 	int n, len;
11222654012fSReza Sabdar 
11232654012fSReza Sabdar 	n = read(session->ns_tape.td_fd, buf, wanted);
11242654012fSReza Sabdar 	if (n < 0) {
11252654012fSReza Sabdar 		/*
11262654012fSReza Sabdar 		 * This fix is for Symantec during importing
11272654012fSReza Sabdar 		 * of spanned data between the tapes.
11282654012fSReza Sabdar 		 */
11292654012fSReza Sabdar 		if (errno == ENOSPC) {
11302654012fSReza Sabdar 			reply->error = NDMP_EOF_ERR;
11312654012fSReza Sabdar 		} else {
11322654012fSReza Sabdar 			NDMP_LOG(LOG_ERR, "Tape read error: %m.");
11332654012fSReza Sabdar 			reply->error = NDMP_IO_ERR;
11342654012fSReza Sabdar 		}
11352654012fSReza Sabdar 	} else if (n == 0) {
11362654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "NDMP_EOF_ERR");
11372654012fSReza Sabdar 
11382654012fSReza Sabdar 		reply->error = NDMP_EOF_ERR;
11392654012fSReza Sabdar 
11402654012fSReza Sabdar 		(void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
11412654012fSReza Sabdar 
11422654012fSReza Sabdar 		len = strlen(NDMP_EOM_MAGIC);
11432654012fSReza Sabdar 		(void) memset(buf, 0, len);
11442654012fSReza Sabdar 		n = read(session->ns_tape.td_fd, buf, len);
11452654012fSReza Sabdar 		buf[len] = '\0';
11462654012fSReza Sabdar 
11472654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "Checking EOM: nread %d [%s]", n, buf);
11482654012fSReza Sabdar 
11492654012fSReza Sabdar 		(void) ndmp_mtioctl(session->ns_tape.td_fd, MTBSF, 1);
11502654012fSReza Sabdar 
11512654012fSReza Sabdar 		if (strncmp(buf, NDMP_EOM_MAGIC, len) != 0)
11522654012fSReza Sabdar 			(void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
11532654012fSReza Sabdar 	} else {
11542654012fSReza Sabdar 		session->ns_tape.td_pos += n;
11552654012fSReza Sabdar 		reply->data_in.data_in_len = n;
11562654012fSReza Sabdar 		reply->data_in.data_in_val = buf;
11572654012fSReza Sabdar 		reply->error = NDMP_NO_ERR;
11582654012fSReza Sabdar 		NS_ADD(rtape, n);
11592654012fSReza Sabdar 	}
11602654012fSReza Sabdar }
11612654012fSReza Sabdar 
11622654012fSReza Sabdar 
11632654012fSReza Sabdar /*
11642654012fSReza Sabdar  * validmode
11652654012fSReza Sabdar  *
11662654012fSReza Sabdar  * Check the tape read mode is valid
11672654012fSReza Sabdar  */
11682654012fSReza Sabdar static boolean_t
validmode(int mode)11692654012fSReza Sabdar validmode(int mode)
11702654012fSReza Sabdar {
11712654012fSReza Sabdar 	boolean_t rv;
11722654012fSReza Sabdar 
11732654012fSReza Sabdar 	switch (mode) {
11742654012fSReza Sabdar 	case NDMP_TAPE_READ_MODE:
11752654012fSReza Sabdar 	case NDMP_TAPE_WRITE_MODE:
11762654012fSReza Sabdar 	case NDMP_TAPE_RAW1_MODE:
11772654012fSReza Sabdar 	case NDMP_TAPE_RAW2_MODE:
11782654012fSReza Sabdar 		rv = TRUE;
11792654012fSReza Sabdar 		break;
11802654012fSReza Sabdar 	default:
11812654012fSReza Sabdar 		rv = FALSE;
11822654012fSReza Sabdar 	}
11832654012fSReza Sabdar 
11842654012fSReza Sabdar 	return (rv);
11852654012fSReza Sabdar }
11862654012fSReza Sabdar 
11872654012fSReza Sabdar 
11882654012fSReza Sabdar /*
11892654012fSReza Sabdar  * common_tape_open
11902654012fSReza Sabdar  *
11912654012fSReza Sabdar  * Generic function for opening the tape for all versions
11922654012fSReza Sabdar  *
11932654012fSReza Sabdar  * Parameters:
11942654012fSReza Sabdar  *   connection (input) - connection handle.
11952654012fSReza Sabdar  *   devname (input) - tape device name to open.
11962654012fSReza Sabdar  *   ndmpmode (input) - mode of opening (read, write, raw)
11972654012fSReza Sabdar  *
11982654012fSReza Sabdar  * Returns:
11992654012fSReza Sabdar  *   void
12002654012fSReza Sabdar  */
12012654012fSReza Sabdar static void
common_tape_open(ndmp_connection_t * connection,char * devname,int ndmpmode)12022654012fSReza Sabdar common_tape_open(ndmp_connection_t *connection, char *devname, int ndmpmode)
12032654012fSReza Sabdar {
12042654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
12052654012fSReza Sabdar 	char adptnm[SCSI_MAX_NAME];
12062654012fSReza Sabdar 	int err;
12072654012fSReza Sabdar 	int mode;
12082654012fSReza Sabdar 	int sid, lun;
12092654012fSReza Sabdar 	scsi_adapter_t *sa;
12102654012fSReza Sabdar 	int devid;
12112654012fSReza Sabdar 
12122654012fSReza Sabdar 	err = NDMP_NO_ERR;
12132654012fSReza Sabdar 
12142654012fSReza Sabdar 	if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) {
12152654012fSReza Sabdar 		NDMP_LOG(LOG_INFO,
12162654012fSReza Sabdar 		    "Connection already has a tape or scsi device open");
12172654012fSReza Sabdar 		err = NDMP_DEVICE_OPENED_ERR;
12182654012fSReza Sabdar 	} else if (!validmode(ndmpmode))
12192654012fSReza Sabdar 		err = NDMP_ILLEGAL_ARGS_ERR;
12202654012fSReza Sabdar 	if ((sa = scsi_get_adapter(0)) != NULL) {
12212654012fSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "Adapter device opened: %s", devname);
12222654012fSReza Sabdar 		(void) strlcpy(adptnm, devname, SCSI_MAX_NAME-2);
12232654012fSReza Sabdar 		adptnm[SCSI_MAX_NAME-1] = '\0';
12242654012fSReza Sabdar 		sid = lun = -1;
12252654012fSReza Sabdar 	}
12262654012fSReza Sabdar 	if (sa) {
12272654012fSReza Sabdar 		scsi_find_sid_lun(sa, devname, &sid, &lun);
12282654012fSReza Sabdar 		if (ndmp_open_list_find(devname, sid, lun) == 0 &&
12292654012fSReza Sabdar 		    (devid = open(devname, O_RDWR | O_NDELAY)) < 0) {
12302654012fSReza Sabdar 			NDMP_LOG(LOG_ERR,
12312654012fSReza Sabdar 			    "Failed to open device %s: %m.", devname);
12322654012fSReza Sabdar 			err = NDMP_NO_DEVICE_ERR;
12332654012fSReza Sabdar 		} else {
12342654012fSReza Sabdar 			(void) close(devid);
12352654012fSReza Sabdar 		}
12362654012fSReza Sabdar 	} else {
12372654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "%s: No such tape device.", devname);
12382654012fSReza Sabdar 		err = NDMP_NO_DEVICE_ERR;
12392654012fSReza Sabdar 	}
12402654012fSReza Sabdar 
12412654012fSReza Sabdar 	if (err != NDMP_NO_ERR) {
12422654012fSReza Sabdar 		tape_open_send_reply(connection, err);
12432654012fSReza Sabdar 		return;
12442654012fSReza Sabdar 	}
12452654012fSReza Sabdar 
12462654012fSReza Sabdar 	/*
12472654012fSReza Sabdar 	 * If tape is not opened in raw mode and tape is not loaded
12482654012fSReza Sabdar 	 * return error.
12492654012fSReza Sabdar 	 */
12502654012fSReza Sabdar 	if (ndmpmode != NDMP_TAPE_RAW1_MODE &&
12512654012fSReza Sabdar 	    ndmpmode != NDMP_TAPE_RAW2_MODE &&
12522654012fSReza Sabdar 	    !is_tape_unit_ready(adptnm, 0)) {
12532654012fSReza Sabdar 		tape_open_send_reply(connection, NDMP_NO_TAPE_LOADED_ERR);
12542654012fSReza Sabdar 		return;
12552654012fSReza Sabdar 	}
12562654012fSReza Sabdar 
12572654012fSReza Sabdar 	mode = (ndmpmode == NDMP_TAPE_READ_MODE) ? O_RDONLY : O_RDWR;
12582654012fSReza Sabdar 	mode |= O_NDELAY;
12592654012fSReza Sabdar 	session->ns_tape.td_fd = open(devname, mode);
12602654012fSReza Sabdar 	if (session->ns_protocol_version == NDMPV4 &&
12612654012fSReza Sabdar 	    session->ns_tape.td_fd < 0 &&
12622654012fSReza Sabdar 	    ndmpmode == NDMP_TAPE_RAW_MODE && errno == EACCES) {
12632654012fSReza Sabdar 		/*
12642654012fSReza Sabdar 		 * V4 suggests that if the tape is open in raw mode
12652654012fSReza Sabdar 		 * and could not be opened with write access, it should
12662654012fSReza Sabdar 		 * be opened read only instead.
12672654012fSReza Sabdar 		 */
12682654012fSReza Sabdar 		ndmpmode = NDMP_TAPE_READ_MODE;
12692654012fSReza Sabdar 		session->ns_tape.td_fd = open(devname, O_RDONLY);
12702654012fSReza Sabdar 	}
12712654012fSReza Sabdar 	if (session->ns_tape.td_fd < 0) {
12722654012fSReza Sabdar 		NDMP_LOG(LOG_ERR, "Failed to open tape device %s: %m.",
12732654012fSReza Sabdar 		    devname);
12742654012fSReza Sabdar 		switch (errno) {
12752654012fSReza Sabdar 		case EACCES:
12762654012fSReza Sabdar 			err = NDMP_WRITE_PROTECT_ERR;
12772654012fSReza Sabdar 			break;
12782654012fSReza Sabdar 		case ENOENT:
12792654012fSReza Sabdar 			err = NDMP_NO_DEVICE_ERR;
12802654012fSReza Sabdar 			break;
12812654012fSReza Sabdar 		case EBUSY:
12822654012fSReza Sabdar 			err = NDMP_DEVICE_BUSY_ERR;
12832654012fSReza Sabdar 			break;
12842654012fSReza Sabdar 		case EPERM:
12852654012fSReza Sabdar 			err = NDMP_PERMISSION_ERR;
12862654012fSReza Sabdar 			break;
12872654012fSReza Sabdar 		default:
12882654012fSReza Sabdar 			err = NDMP_IO_ERR;
12892654012fSReza Sabdar 		}
12902654012fSReza Sabdar 
12912654012fSReza Sabdar 		tape_open_send_reply(connection, err);
12922654012fSReza Sabdar 		return;
12932654012fSReza Sabdar 	}
12942654012fSReza Sabdar 
12952654012fSReza Sabdar 	switch (ndmp_open_list_add(connection,
12962654012fSReza Sabdar 	    adptnm, sid, lun, session->ns_tape.td_fd)) {
12972654012fSReza Sabdar 	case 0:
12982654012fSReza Sabdar 		err = NDMP_NO_ERR;
12992654012fSReza Sabdar 		break;
13002654012fSReza Sabdar 	case EBUSY:
13012654012fSReza Sabdar 		err = NDMP_DEVICE_BUSY_ERR;
13022654012fSReza Sabdar 		break;
13032654012fSReza Sabdar 	case ENOMEM:
13042654012fSReza Sabdar 		err = NDMP_NO_MEM_ERR;
13052654012fSReza Sabdar 		break;
13062654012fSReza Sabdar 	default:
13072654012fSReza Sabdar 		err = NDMP_IO_ERR;
13082654012fSReza Sabdar 	}
13092654012fSReza Sabdar 	if (err != NDMP_NO_ERR) {
13102654012fSReza Sabdar 		tape_open_send_reply(connection, err);
13112654012fSReza Sabdar 		return;
13122654012fSReza Sabdar 	}
13132654012fSReza Sabdar 
13142654012fSReza Sabdar 	session->ns_tape.td_mode = ndmpmode;
13152654012fSReza Sabdar 	session->ns_tape.td_sid = sid;
13162654012fSReza Sabdar 	session->ns_tape.td_lun = lun;
13172654012fSReza Sabdar 	(void) strlcpy(session->ns_tape.td_adapter_name, adptnm, SCSI_MAX_NAME);
13182654012fSReza Sabdar 	session->ns_tape.td_record_count = 0;
13192654012fSReza Sabdar 
13202654012fSReza Sabdar 	NDMP_LOG(LOG_DEBUG, "Tape is opened fd: %d", session->ns_tape.td_fd);
13212654012fSReza Sabdar 
13222654012fSReza Sabdar 	tape_open_send_reply(connection, NDMP_NO_ERR);
13232654012fSReza Sabdar }
13242654012fSReza Sabdar 
13252654012fSReza Sabdar 
13262654012fSReza Sabdar /*
13272654012fSReza Sabdar  * common_tape_close
13282654012fSReza Sabdar  *
13292654012fSReza Sabdar  * Generic function for closing the tape
13302654012fSReza Sabdar  *
13312654012fSReza Sabdar  * Parameters:
13322654012fSReza Sabdar  *   connection (input) - connection handle.
13332654012fSReza Sabdar  *
13342654012fSReza Sabdar  * Returns:
13352654012fSReza Sabdar  *   void
13362654012fSReza Sabdar  */
13372654012fSReza Sabdar static void
common_tape_close(ndmp_connection_t * connection)13382654012fSReza Sabdar common_tape_close(ndmp_connection_t *connection)
13392654012fSReza Sabdar {
13402654012fSReza Sabdar 	ndmpd_session_t *session = ndmp_get_client_data(connection);
13412654012fSReza Sabdar 	ndmp_tape_close_reply reply;
13422654012fSReza Sabdar 
13432654012fSReza Sabdar 	(void) ndmp_open_list_del(session->ns_tape.td_adapter_name,
13442654012fSReza Sabdar 	    session->ns_tape.td_sid, session->ns_tape.td_lun);
13452654012fSReza Sabdar 	(void) close(session->ns_tape.td_fd);
13462654012fSReza Sabdar 	session->ns_tape.td_fd = -1;
13472654012fSReza Sabdar 	session->ns_tape.td_sid = 0;
13482654012fSReza Sabdar 	session->ns_tape.td_lun = 0;
13492654012fSReza Sabdar 	(void) memset(session->ns_tape.td_adapter_name, 0,
13502654012fSReza Sabdar 	    sizeof (session->ns_tape.td_adapter_name));
13512654012fSReza Sabdar 	session->ns_tape.td_record_count = 0;
13522654012fSReza Sabdar 
13532654012fSReza Sabdar 	reply.error = NDMP_NO_ERR;
13542654012fSReza Sabdar 	ndmp_send_reply(connection, (void *) &reply,
13552654012fSReza Sabdar 	    "sending tape_close reply");
13562654012fSReza Sabdar }
13572654012fSReza Sabdar 
13582654012fSReza Sabdar /*
13592654012fSReza Sabdar  * tape_open
13602654012fSReza Sabdar  *
13612654012fSReza Sabdar  * Will try to open the tape with the given flags and
13622654012fSReza Sabdar  * path using the given retries and delay intervals
13632654012fSReza Sabdar  */
13642654012fSReza Sabdar int
tape_open(char * path,int flags)13652654012fSReza Sabdar tape_open(char *path, int flags)
13662654012fSReza Sabdar {
13672654012fSReza Sabdar 	int fd;
13682654012fSReza Sabdar 	int i = 0;
13692654012fSReza Sabdar 
13702654012fSReza Sabdar 	while ((fd = open(path, flags)) == -1 &&
13712654012fSReza Sabdar 	    i++ < ndmp_tape_open_retries) {
13722654012fSReza Sabdar 		if (errno != EBUSY)
13732654012fSReza Sabdar 			break;
13742654012fSReza Sabdar 		(void) usleep(ndmp_tape_open_delay);
13752654012fSReza Sabdar 	}
13762654012fSReza Sabdar 	return (fd);
13772654012fSReza Sabdar }
1378