xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c (revision 86c48bbfeb72d5a6ee171e713059939bab658b77)
1 /*
2  * Copyright 2009 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 
42 #include <sys/param.h>
43 #include <fcntl.h>
44 #include <sys/mtio.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include "ndmpd_common.h"
50 #include "ndmpd.h"
51 
52 static void tape_open_send_reply(ndmp_connection_t *connection, int err);
53 static void unbuffered_read(ndmpd_session_t *session, char *buf, long wanted,
54     ndmp_tape_read_reply *reply);
55 static boolean_t validmode(int mode);
56 static void common_tape_open(ndmp_connection_t *connection, char *devname,
57     int ndmpmode);
58 static void common_tape_close(ndmp_connection_t *connection);
59 
60 /*
61  * Configurable delay & time when the tape is
62  * busy during opening the tape.
63  */
64 int ndmp_tape_open_retries = 5;
65 int ndmp_tape_open_delay = 1000;
66 
67 /*
68  * ************************************************************************
69  * NDMP V2 HANDLERS
70  * ************************************************************************
71  */
72 
73 /*
74  * ndmpd_tape_open_v2
75  *
76  * This handler opens the specified tape device.
77  *
78  * Parameters:
79  *   connection (input) - connection handle.
80  *   body       (input) - request message body.
81  *
82  * Returns:
83  *   void
84  */
85 void
86 ndmpd_tape_open_v2(ndmp_connection_t *connection, void *body)
87 {
88 	ndmp_tape_open_request_v2 *request = (ndmp_tape_open_request_v2 *) body;
89 	ndmpd_session_t *session = ndmp_get_client_data(connection);
90 	char adptnm[SCSI_MAX_NAME];
91 	int mode;
92 	int sid, lun;
93 	int err;
94 	scsi_adapter_t *sa;
95 	int devid;
96 
97 	err = NDMP_NO_ERR;
98 
99 	if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) {
100 		NDMP_LOG(LOG_INFO,
101 		    "Connection already has a tape or scsi device open");
102 		err = NDMP_DEVICE_OPENED_ERR;
103 	} else if (request->mode != NDMP_TAPE_READ_MODE &&
104 	    request->mode != NDMP_TAPE_WRITE_MODE &&
105 	    request->mode != NDMP_TAPE_RAW1_MODE) {
106 		err = NDMP_ILLEGAL_ARGS_ERR;
107 	}
108 
109 	if ((sa = scsi_get_adapter(0)) != NULL) {
110 		NDMP_LOG(LOG_DEBUG,
111 		    "Adapter device opened: %s", request->device.name);
112 		(void) strlcpy(adptnm, request->device.name, SCSI_MAX_NAME-2);
113 		adptnm[SCSI_MAX_NAME-1] = '\0';
114 		sid = lun = -1;
115 	}
116 	/* try to get the scsi id etc.... */
117 	if (sa) {
118 		scsi_find_sid_lun(sa, request->device.name, &sid, &lun);
119 		if (ndmp_open_list_find(request->device.name, sid, lun) == 0 &&
120 		    (devid = tape_open(request->device.name,
121 		    O_RDWR | O_NDELAY)) < 0) {
122 			NDMP_LOG(LOG_ERR, "Failed to open device %s: %m.",
123 			    request->device.name);
124 			err = NDMP_NO_DEVICE_ERR;
125 		}
126 		else
127 			(void) close(devid);
128 	} else {
129 		NDMP_LOG(LOG_ERR, "%s: No such tape device.",
130 		    request->device.name);
131 		err = NDMP_NO_DEVICE_ERR;
132 	}
133 	if (err != NDMP_NO_ERR) {
134 		tape_open_send_reply(connection, err);
135 		return;
136 	}
137 
138 	switch (ndmp_open_list_add(connection, adptnm, sid, lun, devid)) {
139 	case 0:
140 		err = NDMP_NO_ERR;
141 		break;
142 	case EBUSY:
143 		err = NDMP_DEVICE_BUSY_ERR;
144 		break;
145 	case ENOMEM:
146 		err = NDMP_NO_MEM_ERR;
147 		break;
148 	default:
149 		err = NDMP_IO_ERR;
150 	}
151 	if (err != NDMP_NO_ERR) {
152 		tape_open_send_reply(connection, err);
153 		return;
154 	}
155 
156 	/*
157 	 * According to Connectathon 2001, the 0x7fffffff is a secret
158 	 * code between "Workstartion Solutions" and * net_app.
159 	 * If mode is set to this value, tape_open() won't fail if
160 	 * the tape device is not ready.
161 	 */
162 	if (request->mode != NDMP_TAPE_RAW1_MODE &&
163 	    !is_tape_unit_ready(adptnm, 0)) {
164 		(void) ndmp_open_list_del(adptnm, sid, lun);
165 		tape_open_send_reply(connection, NDMP_NO_TAPE_LOADED_ERR);
166 		return;
167 	}
168 
169 	mode = (request->mode == NDMP_TAPE_READ_MODE) ? O_RDONLY : O_RDWR;
170 	mode |= O_NDELAY;
171 	if ((session->ns_tape.td_fd = open(request->device.name, mode)) < 0) {
172 			NDMP_LOG(LOG_ERR, "Failed to open tape device %s: %m.",
173 			    request->device.name);
174 			switch (errno) {
175 			case EACCES:
176 				err = NDMP_WRITE_PROTECT_ERR;
177 				break;
178 			case ENXIO:
179 			case ENOENT:
180 				err = NDMP_NO_DEVICE_ERR;
181 				break;
182 			case EBUSY:
183 				err = NDMP_DEVICE_BUSY_ERR;
184 				break;
185 			default:
186 				err = NDMP_IO_ERR;
187 			}
188 
189 			(void) ndmp_open_list_del(adptnm, sid, lun);
190 			tape_open_send_reply(connection, err);
191 			return;
192 		}
193 
194 	session->ns_tape.td_mode = request->mode;
195 	session->ns_tape.td_sid = sid;
196 	session->ns_tape.td_lun = lun;
197 	(void) strlcpy(session->ns_tape.td_adapter_name, adptnm, SCSI_MAX_NAME);
198 	session->ns_tape.td_record_count = 0;
199 	session->ns_tape.td_eom_seen = FALSE;
200 
201 	NDMP_LOG(LOG_DEBUG, "Tape is opened fd: %d", session->ns_tape.td_fd);
202 
203 	tape_open_send_reply(connection, NDMP_NO_ERR);
204 }
205 
206 
207 /*
208  * ndmpd_tape_close_v2
209  *
210  * This handler closes the currently open tape device.
211  *
212  * Parameters:
213  *   connection (input) - connection handle.
214  *   body       (input) - request message body.
215  *
216  * Returns:
217  *   void
218  */
219 /*ARGSUSED*/
220 void
221 ndmpd_tape_close_v2(ndmp_connection_t *connection, void *body)
222 {
223 	ndmp_tape_close_reply reply;
224 	ndmpd_session_t *session = ndmp_get_client_data(connection);
225 
226 	if (session->ns_tape.td_fd == -1) {
227 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
228 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
229 		ndmp_send_reply(connection, (void *) &reply,
230 		    "sending tape_close reply");
231 		return;
232 	}
233 	common_tape_close(connection);
234 
235 }
236 
237 /*
238  * ndmpd_tape_get_state_v2
239  *
240  * This handler handles the tape_get_state request.
241  * Status information for the currently open tape device is returned.
242  *
243  * Parameters:
244  *   connection (input) - connection handle.
245  *   body       (input) - request message body.
246  *
247  * Returns:
248  *   void
249  */
250 /*ARGSUSED*/
251 void
252 ndmpd_tape_get_state_v2(ndmp_connection_t *connection, void *body)
253 
254 {
255 	ndmp_tape_get_state_reply_v2 reply;
256 	ndmpd_session_t *session = ndmp_get_client_data(connection);
257 	struct mtget mtstatus;
258 	struct mtdrivetype_request dtpr;
259 	struct mtdrivetype dtp;
260 
261 	if (session->ns_tape.td_fd == -1) {
262 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
263 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
264 		ndmp_send_reply(connection, (void *) &reply,
265 		    "sending tape_get_state reply");
266 		return;
267 	}
268 
269 	if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) < 0) {
270 		NDMP_LOG(LOG_ERR, "Failed to get status from tape: %m.");
271 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGET) error: %m.");
272 		reply.error = NDMP_IO_ERR;
273 		ndmp_send_reply(connection, (void *)&reply,
274 		    "sending tape_get_state reply");
275 		return;
276 	}
277 
278 	dtpr.size = sizeof (struct mtdrivetype);
279 	dtpr.mtdtp = &dtp;
280 	if (ioctl(session->ns_tape.td_fd, MTIOCGETDRIVETYPE, &dtpr) == -1) {
281 		NDMP_LOG(LOG_ERR,
282 		    "Failed to get drive type information from tape: %m.");
283 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGETDRIVETYPE) error: %m.");
284 		reply.error = NDMP_IO_ERR;
285 		ndmp_send_reply(connection, (void *)&reply,
286 		    "sending tape_get_state reply");
287 		return;
288 	}
289 
290 	reply.flags = 0;
291 
292 	reply.file_num = mtstatus.mt_fileno;
293 	reply.soft_errors = 0;
294 	reply.block_size = dtp.bsize;
295 	if (dtp.bsize == 0)
296 		reply.blockno = mtstatus.mt_blkno;
297 	else
298 		reply.blockno = mtstatus.mt_blkno *
299 		    (session->ns_mover.md_record_size / dtp.bsize);
300 
301 	reply.soft_errors = 0;
302 	reply.total_space = long_long_to_quad(0);	/* not supported */
303 	reply.space_remain = long_long_to_quad(0);	/* not supported */
304 
305 	NDMP_LOG(LOG_DEBUG,
306 	    "flags: 0x%x, file_num: %d, block_size: %d, blockno: %d",
307 	    reply.flags, reply.file_num, reply.block_size, reply.blockno);
308 
309 	reply.error = NDMP_NO_ERR;
310 	ndmp_send_reply(connection, (void *) &reply,
311 	    "sending tape_get_state reply");
312 }
313 
314 
315 /*
316  * ndmpd_tape_mtio_v2
317  *
318  * This handler handles tape_mtio requests.
319  *
320  * Parameters:
321  *   connection (input) - connection handle.
322  *   body       (input) - request message body.
323  *
324  * Returns:
325  *   void
326  */
327 void
328 ndmpd_tape_mtio_v2(ndmp_connection_t *connection, void *body)
329 {
330 	ndmp_tape_mtio_request *request = (ndmp_tape_mtio_request *) body;
331 	ndmp_tape_mtio_reply reply;
332 	ndmpd_session_t *session = ndmp_get_client_data(connection);
333 
334 	struct mtop tapeop;
335 	struct mtget mtstatus;
336 	int retry = 0;
337 	int rc;
338 
339 	reply.resid_count = 0;
340 
341 	if (session->ns_tape.td_fd == -1) {
342 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
343 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
344 		ndmp_send_reply(connection, (void *) &reply,
345 		    "sending tape_mtio reply");
346 		return;
347 	}
348 
349 	reply.error = NDMP_NO_ERR;
350 	switch (request->tape_op) {
351 	case NDMP_MTIO_FSF:
352 		tapeop.mt_op = MTFSF;
353 		break;
354 	case NDMP_MTIO_BSF:
355 		tapeop.mt_op = MTBSF;
356 		break;
357 	case NDMP_MTIO_FSR:
358 		tapeop.mt_op = MTFSR;
359 		break;
360 	case NDMP_MTIO_BSR:
361 		tapeop.mt_op = MTBSR;
362 		break;
363 	case NDMP_MTIO_REW:
364 		tapeop.mt_op = MTREW;
365 		break;
366 	case NDMP_MTIO_EOF:
367 		if (session->ns_tape.td_mode == NDMP_TAPE_READ_MODE)
368 			reply.error = NDMP_PERMISSION_ERR;
369 		tapeop.mt_op = MTWEOF;
370 		break;
371 	case NDMP_MTIO_OFF:
372 		tapeop.mt_op = MTOFFL;
373 		break;
374 
375 	case NDMP_MTIO_TUR: /* test unit ready */
376 
377 		if (is_tape_unit_ready(session->ns_tape.td_adapter_name,
378 		    session->ns_tape.td_fd) == 0)
379 			/* tape not ready ? */
380 			reply.error = NDMP_NO_TAPE_LOADED_ERR;
381 		break;
382 
383 	default:
384 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
385 	}
386 
387 	if (reply.error == NDMP_NO_ERR && request->tape_op != NDMP_MTIO_TUR) {
388 		tapeop.mt_count = request->count;
389 
390 		do {
391 			NS_UPD(twait, trun);
392 			rc = ioctl(session->ns_tape.td_fd, MTIOCTOP, &tapeop);
393 			NS_UPD(trun, twait);
394 			NDMP_LOG(LOG_DEBUG,
395 			    "ioctl MTIO rc:%d, cmd:%d, retry:%d, error: %d",
396 			    rc, tapeop.mt_op, retry, errno);
397 		} while (rc < 0 && errno == EIO &&
398 		    retry++ < 5);
399 
400 		/*
401 		 * Ignore I/O errors since these usually are the result of
402 		 * attempting to position past the beginning or end of the tape.
403 		 * The residual count will be returned and can be used to
404 		 * determine that the call was not completely successful.
405 		 */
406 		if (rc < 0) {
407 			NDMP_LOG(LOG_ERR,
408 			    "Failed to send command to tape: %m.");
409 			NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCTOP) error: %m.");
410 
411 			/* MTWEOF doesnt have residual count */
412 			if (tapeop.mt_op == MTWEOF)
413 				reply.error = NDMP_IO_ERR;
414 			else
415 				reply.error = NDMP_NO_ERR;
416 			reply.resid_count = tapeop.mt_count;
417 			ndmp_send_reply(connection, (void *)&reply,
418 			    "sending tape_mtio reply");
419 			return;
420 		}
421 
422 		if (request->tape_op != NDMP_MTIO_REW &&
423 		    request->tape_op != NDMP_MTIO_OFF) {
424 			if (ioctl(session->ns_tape.td_fd, MTIOCGET,
425 			    &mtstatus) < 0) {
426 				NDMP_LOG(LOG_ERR,
427 				    "Failed to send command to tape: %m.");
428 				NDMP_LOG(LOG_DEBUG,
429 				    "ioctl(MTIOCGET) error: %m.");
430 				reply.error = NDMP_IO_ERR;
431 				ndmp_send_reply(connection, (void *)&reply,
432 				    "sending tape_mtio reply");
433 
434 				return;
435 			}
436 
437 			reply.resid_count = labs(mtstatus.mt_resid);
438 		}
439 	}
440 
441 	NDMP_LOG(LOG_DEBUG, "resid_count: %d",
442 	    reply.resid_count);
443 	ndmp_send_reply(connection, (void *) &reply, "sending tape_mtio reply");
444 }
445 
446 
447 /*
448  * ndmpd_tape_write_v2
449  *
450  * This handler handles tape_write requests.
451  * This interface is a non-buffered interface. Each write request
452  * maps directly to a write to the tape device. It is the responsibility
453  * of the NDMP client to pad the data to the desired record size.
454  * It is the responsibility of the NDMP client to ensure that the
455  * length is a multiple of the tape block size if the tape device
456  * is in fixed block mode.
457  *
458  * Parameters:
459  *   connection (input) - connection handle.
460  *   body       (input) - request message body.
461  *
462  * Returns:
463  *   void
464  */
465 void
466 ndmpd_tape_write_v2(ndmp_connection_t *connection, void *body)
467 {
468 	ndmp_tape_write_request *request = (ndmp_tape_write_request *) body;
469 	ndmp_tape_write_reply reply;
470 	ndmpd_session_t *session = ndmp_get_client_data(connection);
471 	ssize_t n;
472 
473 	reply.count = 0;
474 
475 	if (session->ns_tape.td_fd == -1) {
476 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
477 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
478 		ndmp_send_reply(connection, (void *) &reply,
479 		    "sending tape_write reply");
480 		return;
481 	}
482 	if (session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
483 		NDMP_LOG(LOG_INFO, "Tape device opened in read-only mode");
484 		reply.error = NDMP_PERMISSION_ERR;
485 		ndmp_send_reply(connection, (void *) &reply,
486 		    "sending tape_write reply");
487 		return;
488 	}
489 	if (request->data_out.data_out_len == 0) {
490 		reply.error = NDMP_NO_ERR;
491 		ndmp_send_reply(connection, (void *) &reply,
492 		    "sending tape_write reply");
493 		return;
494 	}
495 
496 	if (session->ns_tape.td_eom_seen) {
497 		/*
498 		 * Refer to the comment at the top of this file for
499 		 * Mammoth2 tape drives.
500 		 */
501 		NDMP_LOG(LOG_DEBUG, "eom_seen");
502 		ndmpd_write_eom(session->ns_tape.td_fd);
503 
504 		session->ns_tape.td_eom_seen = FALSE;
505 		reply.error = NDMP_EOM_ERR;
506 		ndmp_send_reply(connection, (void *) &reply,
507 		    "sending tape_write reply");
508 		return;
509 	}
510 
511 	n = write(session->ns_tape.td_fd, request->data_out.data_out_val,
512 	    request->data_out.data_out_len);
513 	if (n >= 0) {
514 		session->ns_tape.td_write = 1;
515 		NS_ADD(wtape, n);
516 	}
517 	if (n == 0) {
518 		NDMP_LOG(LOG_DEBUG, "n == 0");
519 		reply.error = NDMP_EOM_ERR;
520 		session->ns_tape.td_eom_seen = FALSE;
521 	} else if (n < 0) {
522 		NDMP_LOG(LOG_ERR, "Tape write error: %m.");
523 		reply.error = NDMP_IO_ERR;
524 	} else {
525 		reply.count = n;
526 		reply.error = NDMP_NO_ERR;
527 
528 		/*
529 		 * a logical end of tape will return number of bytes written
530 		 * less than rquested, and one more request to write will
531 		 * give 0, and then no-space
532 		 */
533 		if (n < request->data_out.data_out_len) {
534 			NDMP_LOG(LOG_DEBUG, "LEOT: n: %d", n);
535 			session->ns_tape.td_eom_seen = TRUE;
536 		} else {
537 			session->ns_tape.td_eom_seen = FALSE;
538 		}
539 	}
540 	ndmp_send_reply(connection, &reply,
541 	    "sending tape_write reply");
542 }
543 
544 
545 /*
546  * ndmpd_tape_read_v2
547  *
548  * This handler handles tape_read requests.
549  * This interface is a non-buffered interface. Each read request
550  * maps directly to a read to the tape device. It is the responsibility
551  * of the NDMP client to issue read requests with a length that is at
552  * least as large as the record size used write the tape. The tape driver
553  * always reads a full record. Data is discarded if the read request is
554  * smaller than the record size.
555  * It is the responsibility of the NDMP client to ensure that the
556  * length is a multiple of the tape block size if the tape device
557  * is in fixed block mode.
558  *
559  * Parameters:
560  *   connection (input) - connection handle.
561  *   body       (input) - request message body.
562  *
563  * Returns:
564  *   void
565  */
566 void
567 ndmpd_tape_read_v2(ndmp_connection_t *connection, void *body)
568 {
569 	ndmp_tape_read_request *request = (ndmp_tape_read_request *) body;
570 	ndmp_tape_read_reply reply;
571 	ndmpd_session_t *session = ndmp_get_client_data(connection);
572 	char *buf;
573 
574 	reply.data_in.data_in_len = 0;
575 
576 	if (session->ns_tape.td_fd == -1) {
577 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
578 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
579 		ndmp_send_reply(connection, (void *)&reply,
580 		    "sending tape_read reply");
581 		return;
582 	}
583 	if (request->count == 0) {
584 		reply.error = NDMP_NO_ERR;
585 		ndmp_send_reply(connection, (void *)&reply,
586 		    "sending tape_read reply");
587 		return;
588 	}
589 	if ((buf = ndmp_malloc(request->count)) == 0) {
590 		reply.error = NDMP_NO_MEM_ERR;
591 		ndmp_send_reply(connection, (void *)&reply,
592 		    "sending tape_read reply");
593 		return;
594 	}
595 
596 	session->ns_tape.td_eom_seen = FALSE;
597 
598 	unbuffered_read(session, buf, request->count, &reply);
599 
600 	ndmp_send_reply(connection, (void *) &reply, "sending tape_read reply");
601 	(void) free(buf);
602 }
603 
604 
605 /*
606  * ndmpd_tape_execute_cdb_v2
607  *
608  * This handler handles tape_execute_cdb requests.
609  *
610  * Parameters:
611  *   connection (input) - connection handle.
612  *   body       (input) - request message body.
613  *
614  * Returns:
615  *   void
616  */
617 void
618 ndmpd_tape_execute_cdb_v2(ndmp_connection_t *connection, void *body)
619 {
620 	ndmp_tape_execute_cdb_request *request;
621 	ndmp_tape_execute_cdb_reply reply;
622 	ndmpd_session_t *session = ndmp_get_client_data(connection);
623 
624 	request = (ndmp_tape_execute_cdb_request *) body;
625 
626 	if (session->ns_tape.td_fd == -1) {
627 		(void) memset((void *) &reply, 0, sizeof (reply));
628 
629 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
630 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
631 		ndmp_send_reply(connection, (void *) &reply,
632 		    "sending tape_execute_cdb reply");
633 	} else {
634 		session->ns_tape.td_eom_seen = FALSE;
635 		ndmp_execute_cdb(session, session->ns_tape.td_adapter_name,
636 		    session->ns_tape.td_sid, session->ns_tape.td_lun,
637 		    (ndmp_execute_cdb_request *)request);
638 	}
639 }
640 
641 
642 /*
643  * ************************************************************************
644  * NDMP V3 HANDLERS
645  * ************************************************************************
646  */
647 
648 /*
649  * ndmpd_tape_open_v3
650  *
651  * This handler opens the specified tape device.
652  *
653  * Parameters:
654  *   connection (input) - connection handle.
655  *   body       (input) - request message body.
656  *
657  * Returns:
658  *   void
659  */
660 void
661 ndmpd_tape_open_v3(ndmp_connection_t *connection, void *body)
662 {
663 	ndmp_tape_open_request_v3 *request = (ndmp_tape_open_request_v3 *)body;
664 
665 	common_tape_open(connection, request->device, request->mode);
666 }
667 
668 
669 /*
670  * ndmpd_tape_get_state_v3
671  *
672  * This handler handles the ndmp_tape_get_state_request.
673  * Status information for the currently open tape device is returned.
674  *
675  * Parameters:
676  *   connection (input) - connection handle.
677  *   body       (input) - request message body.
678  *
679  * Returns:
680  *   void
681  */
682 /*ARGSUSED*/
683 void
684 ndmpd_tape_get_state_v3(ndmp_connection_t *connection, void *body)
685 {
686 	ndmp_tape_get_state_reply_v3 reply;
687 	ndmpd_session_t *session = ndmp_get_client_data(connection);
688 	struct mtdrivetype_request dtpr;
689 	struct mtdrivetype dtp;
690 	struct mtget mtstatus;
691 
692 	if (session->ns_tape.td_fd == -1) {
693 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
694 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
695 		ndmp_send_reply(connection, (void *) &reply,
696 		    "sending tape_get_state reply");
697 		return;
698 	}
699 
700 	if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == -1) {
701 		NDMP_LOG(LOG_ERR, "Failed to get status from tape: %m.");
702 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGET) error: %m.");
703 
704 		reply.error = NDMP_IO_ERR;
705 		ndmp_send_reply(connection, (void *)&reply,
706 		    "sending tape_get_state reply");
707 		return;
708 	}
709 
710 	dtpr.size = sizeof (struct mtdrivetype);
711 	dtpr.mtdtp = &dtp;
712 	if (ioctl(session->ns_tape.td_fd, MTIOCGETDRIVETYPE, &dtpr) == -1) {
713 		NDMP_LOG(LOG_ERR,
714 		    "Failed to get drive type information from tape: %m.");
715 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGETDRIVETYPE) error: %m.");
716 
717 		reply.error = NDMP_IO_ERR;
718 		ndmp_send_reply(connection, (void *)&reply,
719 		    "sending tape_get_state reply");
720 		return;
721 	}
722 
723 	reply.flags = 0;
724 
725 	reply.file_num = mtstatus.mt_fileno;
726 	reply.soft_errors = 0;
727 	reply.block_size = dtp.bsize;
728 	if (dtp.bsize == 0)
729 		reply.blockno = mtstatus.mt_blkno;
730 	else
731 		reply.blockno = mtstatus.mt_blkno *
732 		    (session->ns_mover.md_record_size / dtp.bsize);
733 	reply.total_space = long_long_to_quad(0); /* not supported */
734 	reply.space_remain = long_long_to_quad(0); /* not supported */
735 	reply.partition = 0; /* not supported */
736 
737 	reply.soft_errors = 0;
738 	reply.total_space = long_long_to_quad(0LL);
739 	reply.space_remain = long_long_to_quad(0LL);
740 
741 	reply.invalid = NDMP_TAPE_STATE_SOFT_ERRORS_INVALID |
742 	    NDMP_TAPE_STATE_TOTAL_SPACE_INVALID |
743 	    NDMP_TAPE_STATE_SPACE_REMAIN_INVALID |
744 	    NDMP_TAPE_STATE_PARTITION_INVALID;
745 
746 
747 	NDMP_LOG(LOG_DEBUG, "f 0x%x, fnum %d, bsize %d, bno: %d",
748 	    reply.flags, reply.file_num, reply.block_size, reply.blockno);
749 
750 	reply.error = NDMP_NO_ERR;
751 	ndmp_send_reply(connection, (void *) &reply,
752 	    "sending tape_get_state reply");
753 }
754 
755 
756 /*
757  * ndmpd_tape_write_v3
758  *
759  * This handler handles tape_write requests.
760  * This interface is a non-buffered interface. Each write request
761  * maps directly to a write to the tape device. It is the responsibility
762  * of the NDMP client to pad the data to the desired record size.
763  * It is the responsibility of the NDMP client to ensure that the
764  * length is a multiple of the tape block size if the tape device
765  * is in fixed block mode.
766  *
767  * Parameters:
768  *   connection (input) - connection handle.
769  *   body       (input) - request message body.
770  *
771  * Returns:
772  *   void
773  */
774 void
775 ndmpd_tape_write_v3(ndmp_connection_t *connection, void *body)
776 {
777 	ndmp_tape_write_request *request = (ndmp_tape_write_request *) body;
778 	ndmp_tape_write_reply reply;
779 	ndmpd_session_t *session = ndmp_get_client_data(connection);
780 	ssize_t n;
781 
782 	reply.count = 0;
783 
784 	if (session->ns_tape.td_fd == -1) {
785 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
786 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
787 		ndmp_send_reply(connection, (void *) &reply,
788 		    "sending tape_write reply");
789 		return;
790 	}
791 	if (session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
792 		NDMP_LOG(LOG_INFO, "Tape device opened in read-only mode");
793 		reply.error = NDMP_PERMISSION_ERR;
794 		ndmp_send_reply(connection, (void *) &reply,
795 		    "sending tape_write reply");
796 		return;
797 	}
798 	if (request->data_out.data_out_len == 0) {
799 		reply.error = NDMP_NO_ERR;
800 		ndmp_send_reply(connection, (void *) &reply,
801 		    "sending tape_write reply");
802 		return;
803 	}
804 
805 	/*
806 	 * V4 suggests that this should not be accepted
807 	 * when mover is in listen or active state
808 	 */
809 	if (session->ns_protocol_version == NDMPV4 &&
810 	    (session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
811 	    session->ns_mover.md_state == NDMP_MOVER_STATE_ACTIVE)) {
812 
813 		reply.error = NDMP_DEVICE_BUSY_ERR;
814 		ndmp_send_reply(connection, (void *) &reply,
815 		    "sending tape_write reply");
816 		return;
817 	}
818 
819 	/*
820 	 * Refer to the comment at the top of this file for
821 	 * Mammoth2 tape drives.
822 	 */
823 	if (session->ns_tape.td_eom_seen) {
824 		NDMP_LOG(LOG_DEBUG, "eom_seen");
825 		ndmpd_write_eom(session->ns_tape.td_fd);
826 		session->ns_tape.td_eom_seen = FALSE;
827 		reply.error = NDMP_EOM_ERR;
828 		ndmp_send_reply(connection, (void *) &reply,
829 		    "sending tape_write reply");
830 		return;
831 	}
832 
833 	n = write(session->ns_tape.td_fd, request->data_out.data_out_val,
834 	    request->data_out.data_out_len);
835 
836 	session->ns_tape.td_eom_seen = FALSE;
837 	if (n >= 0) {
838 		session->ns_tape.td_write = 1;
839 		NS_ADD(wtape, n);
840 	}
841 	if (n == 0) {
842 		NDMP_LOG(LOG_INFO, "EOM detected");
843 		reply.error = NDMP_EOM_ERR;
844 		session->ns_tape.td_eom_seen = TRUE;
845 	} else if (n < 0) {
846 		NDMP_LOG(LOG_ERR, "Tape write error: %m.");
847 		reply.error = NDMP_IO_ERR;
848 	} else {
849 		reply.count = n;
850 		reply.error = NDMP_NO_ERR;
851 	}
852 
853 	ndmp_send_reply(connection, (void *) &reply,
854 	    "sending tape_write reply");
855 }
856 
857 
858 /*
859  * ndmpd_tape_read_v3
860  *
861  * This handler handles tape_read requests.
862  * This interface is a non-buffered interface. Each read request
863  * maps directly to a read to the tape device. It is the responsibility
864  * of the NDMP client to issue read requests with a length that is at
865  * least as large as the record size used write the tape. The tape driver
866  * always reads a full record. Data is discarded if the read request is
867  * smaller than the record size.
868  * It is the responsibility of the NDMP client to ensure that the
869  * length is a multiple of the tape block size if the tape device
870  * is in fixed block mode.
871  *
872  * Parameters:
873  *   connection (input) - connection handle.
874  *   body       (input) - request message body.
875  *
876  * Returns:
877  *   void
878  */
879 void
880 ndmpd_tape_read_v3(ndmp_connection_t *connection, void *body)
881 {
882 	ndmp_tape_read_request *request = (ndmp_tape_read_request *) body;
883 	ndmp_tape_read_reply reply;
884 	ndmpd_session_t *session = ndmp_get_client_data(connection);
885 	char *buf;
886 	int n, len;
887 
888 	reply.data_in.data_in_len = 0;
889 
890 	if (session->ns_tape.td_fd == -1) {
891 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
892 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
893 		ndmp_send_reply(connection, (void *) &reply,
894 		    "sending tape_read reply");
895 		return;
896 	}
897 	if (request->count == 0) {
898 		reply.error = NDMP_NO_ERR;
899 		ndmp_send_reply(connection, (void *) &reply,
900 		    "sending tape_read reply");
901 		return;
902 	}
903 
904 	/*
905 	 * V4 suggests that this should not be accepted
906 	 * when mover is in listen or active state
907 	 */
908 	if (session->ns_protocol_version == NDMPV4 &&
909 	    (session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
910 	    session->ns_mover.md_state == NDMP_MOVER_STATE_ACTIVE)) {
911 
912 		reply.error = NDMP_DEVICE_BUSY_ERR;
913 		ndmp_send_reply(connection, (void *) &reply,
914 		    "sending tape_read reply");
915 		return;
916 	}
917 
918 	if ((buf = ndmp_malloc(request->count)) == NULL) {
919 		reply.error = NDMP_NO_MEM_ERR;
920 		ndmp_send_reply(connection, (void *) &reply,
921 		    "sending tape_read reply");
922 		return;
923 	}
924 	session->ns_tape.td_eom_seen = FALSE;
925 
926 	n = read(session->ns_tape.td_fd, buf, request->count);
927 	if (n < 0) {
928 		/*
929 		 * This fix is for Symantec during importing
930 		 * of spanned data between the tapes.
931 		 */
932 		if (errno == ENOSPC) {
933 			reply.error = NDMP_EOF_ERR;
934 		} else {
935 			NDMP_LOG(LOG_ERR, "Tape read error: %m.");
936 			reply.error = NDMP_IO_ERR;
937 		}
938 	} else if (n == 0) {
939 		(void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
940 
941 		len = strlen(NDMP_EOM_MAGIC);
942 		(void) memset(buf, 0, len);
943 		n = read(session->ns_tape.td_fd, buf, len);
944 		buf[len] = '\0';
945 
946 		NDMP_LOG(LOG_DEBUG, "Checking EOM: nread %d [%s]", n, buf);
947 
948 		if (strncmp(buf, NDMP_EOM_MAGIC, len) == 0 || n == 0) {
949 			reply.error = NDMP_EOM_ERR;
950 			NDMP_LOG(LOG_DEBUG, "NDMP_EOM_ERR");
951 		} else {
952 			reply.error = NDMP_EOF_ERR;
953 			NDMP_LOG(LOG_DEBUG, "NDMP_EOF_ERR");
954 		}
955 		if (n > 0)
956 			(void) ndmp_mtioctl(session->ns_tape.td_fd, MTBSR, 1);
957 	} else {
958 		/*
959 		 * Symantec fix for import phase
960 		 *
961 		 * As import process from symantec skips filemarks
962 		 * they can come across to NDMP_EOM_MAGIC and treat
963 		 * it as data. This fix prevents the magic to be
964 		 * sent to the client and the read will return zero bytes
965 		 * and set the NDMP_EOM_ERR error. The tape should
966 		 * be positioned at the EOT side of the file mark.
967 		 */
968 		len = strlen(NDMP_EOM_MAGIC);
969 		if (n == len && strncmp(buf, NDMP_EOM_MAGIC, len) == 0) {
970 			reply.error = NDMP_EOM_ERR;
971 			(void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
972 			NDMP_LOG(LOG_DEBUG, "NDMP_EOM_ERR");
973 		} else {
974 			session->ns_tape.td_pos += n;
975 			reply.data_in.data_in_len = n;
976 			reply.data_in.data_in_val = buf;
977 			reply.error = NDMP_NO_ERR;
978 		}
979 		NS_ADD(rtape, n);
980 	}
981 
982 	ndmp_send_reply(connection, (void *) &reply, "sending tape_read reply");
983 	free(buf);
984 }
985 
986 
987 /*
988  * ************************************************************************
989  * NDMP V4 HANDLERS
990  * ************************************************************************
991  */
992 
993 /*
994  * ndmpd_tape_get_state_v4
995  *
996  * This handler handles the ndmp_tape_get_state_request.
997  * Status information for the currently open tape device is returned.
998  *
999  * Parameters:
1000  *   connection (input) - connection handle.
1001  *   body       (input) - request message body.
1002  *
1003  * Returns:
1004  *   void
1005  */
1006 /*ARGSUSED*/
1007 void
1008 ndmpd_tape_get_state_v4(ndmp_connection_t *connection, void *body)
1009 {
1010 	ndmp_tape_get_state_reply_v4 reply;
1011 	ndmpd_session_t *session = ndmp_get_client_data(connection);
1012 	struct mtget mtstatus;
1013 	struct mtdrivetype_request dtpr;
1014 	struct mtdrivetype dtp;
1015 
1016 	if (session->ns_tape.td_fd == -1) {
1017 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
1018 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
1019 		ndmp_send_reply(connection, (void *) &reply,
1020 		    "sending tape_get_state reply");
1021 		return;
1022 	}
1023 
1024 	/*
1025 	 * Need code to detect NDMP_TAPE_STATE_NOREWIND
1026 	 */
1027 
1028 	if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == -1) {
1029 		NDMP_LOG(LOG_ERR,
1030 		    "Failed to get status information from tape: %m.");
1031 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGET) error: %m.");
1032 
1033 		reply.error = NDMP_IO_ERR;
1034 		ndmp_send_reply(connection, (void *)&reply,
1035 		    "sending tape_get_state reply");
1036 		return;
1037 	}
1038 
1039 	dtpr.size = sizeof (struct mtdrivetype);
1040 	dtpr.mtdtp = &dtp;
1041 	if (ioctl(session->ns_tape.td_fd, MTIOCGETDRIVETYPE, &dtpr) == -1) {
1042 		NDMP_LOG(LOG_ERR,
1043 		    "Failed to get drive type information from tape: %m.");
1044 		NDMP_LOG(LOG_DEBUG, "ioctl(MTIOCGETDRIVETYPE) error: %m.");
1045 
1046 		reply.error = NDMP_IO_ERR;
1047 		ndmp_send_reply(connection, (void *)&reply,
1048 		    "sending tape_get_state reply");
1049 		return;
1050 	}
1051 
1052 	reply.flags = NDMP_TAPE_NOREWIND;
1053 
1054 	reply.file_num = mtstatus.mt_fileno;
1055 	reply.soft_errors = 0;
1056 	reply.block_size = dtp.bsize;
1057 
1058 	if (dtp.bsize == 0)
1059 		reply.blockno = mtstatus.mt_blkno;
1060 	else
1061 		reply.blockno = mtstatus.mt_blkno *
1062 		    (session->ns_mover.md_record_size / dtp.bsize);
1063 
1064 	reply.total_space = long_long_to_quad(0); /* not supported */
1065 	reply.space_remain = long_long_to_quad(0); /* not supported */
1066 
1067 	reply.soft_errors = 0;
1068 	reply.total_space = long_long_to_quad(0LL);
1069 	reply.space_remain = long_long_to_quad(0LL);
1070 	reply.unsupported = NDMP_TAPE_STATE_SOFT_ERRORS_INVALID |
1071 	    NDMP_TAPE_STATE_TOTAL_SPACE_INVALID |
1072 	    NDMP_TAPE_STATE_SPACE_REMAIN_INVALID |
1073 	    NDMP_TAPE_STATE_PARTITION_INVALID;
1074 
1075 
1076 	NDMP_LOG(LOG_DEBUG, "f 0x%x, fnum %d, bsize %d, bno: %d",
1077 	    reply.flags, reply.file_num, reply.block_size, reply.blockno);
1078 
1079 	reply.error = NDMP_NO_ERR;
1080 	ndmp_send_reply(connection, (void *) &reply,
1081 	    "sending tape_get_state reply");
1082 }
1083 /*
1084  * ndmpd_tape_close_v4
1085  *
1086  * This handler (v4) closes the currently open tape device.
1087  *
1088  * Parameters:
1089  *   connection (input) - connection handle.
1090  *   body       (input) - request message body.
1091  *
1092  * Returns:
1093  *   void
1094  */
1095 /*ARGSUSED*/
1096 void
1097 ndmpd_tape_close_v4(ndmp_connection_t *connection, void *body)
1098 {
1099 	ndmp_tape_close_reply reply;
1100 	ndmpd_session_t *session = ndmp_get_client_data(connection);
1101 
1102 	if (session->ns_tape.td_fd == -1) {
1103 		NDMP_LOG(LOG_ERR, "Tape device is not open.");
1104 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
1105 		ndmp_send_reply(connection, (void *) &reply,
1106 		    "sending tape_close reply");
1107 		return;
1108 	}
1109 
1110 	/*
1111 	 * V4 suggests that this should not be accepted
1112 	 * when mover is in listen or active state
1113 	 */
1114 	if (session->ns_mover.md_state == NDMP_MOVER_STATE_LISTEN ||
1115 	    session->ns_mover.md_state == NDMP_MOVER_STATE_ACTIVE) {
1116 
1117 		reply.error = NDMP_DEVICE_BUSY_ERR;
1118 		ndmp_send_reply(connection, (void *) &reply,
1119 		    "sending tape_close reply");
1120 		return;
1121 	}
1122 
1123 	common_tape_close(connection);
1124 }
1125 
1126 
1127 /*
1128  * ************************************************************************
1129  * LOCALS
1130  * ************************************************************************
1131  */
1132 /*
1133  * tape_open_send_reply
1134  *
1135  * Send a reply to the tape open message
1136  *
1137  * Parameters:
1138  *   connection (input) - connection handle.
1139  *   err (input) - NDMP error
1140  *
1141  * Returns:
1142  *   void
1143  */
1144 static void
1145 tape_open_send_reply(ndmp_connection_t *connection, int err)
1146 {
1147 	ndmp_tape_open_reply reply;
1148 
1149 	reply.error = err;
1150 	ndmp_send_reply(connection, (void *) &reply, "sending tape_open reply");
1151 }
1152 
1153 /*
1154  * unbuffered_read
1155  *
1156  * Perform tape read without read-ahead
1157  *
1158  * Parameters:
1159  *   session (input) - session handle
1160  *   bp (output) - read buffer
1161  *   wanted (input) - number of bytes wanted
1162  *   reply (output) - tape read reply message
1163  *
1164  * Returns:
1165  *   void
1166  */
1167 static void
1168 unbuffered_read(ndmpd_session_t *session, char *buf, long wanted,
1169     ndmp_tape_read_reply *reply)
1170 {
1171 	int n, len;
1172 
1173 	n = read(session->ns_tape.td_fd, buf, wanted);
1174 	if (n < 0) {
1175 		/*
1176 		 * This fix is for Symantec during importing
1177 		 * of spanned data between the tapes.
1178 		 */
1179 		if (errno == ENOSPC) {
1180 			reply->error = NDMP_EOF_ERR;
1181 		} else {
1182 			NDMP_LOG(LOG_ERR, "Tape read error: %m.");
1183 			reply->error = NDMP_IO_ERR;
1184 		}
1185 	} else if (n == 0) {
1186 		NDMP_LOG(LOG_DEBUG, "NDMP_EOF_ERR");
1187 
1188 		reply->error = NDMP_EOF_ERR;
1189 
1190 		(void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
1191 
1192 		len = strlen(NDMP_EOM_MAGIC);
1193 		(void) memset(buf, 0, len);
1194 		n = read(session->ns_tape.td_fd, buf, len);
1195 		buf[len] = '\0';
1196 
1197 		NDMP_LOG(LOG_DEBUG, "Checking EOM: nread %d [%s]", n, buf);
1198 
1199 		(void) ndmp_mtioctl(session->ns_tape.td_fd, MTBSF, 1);
1200 
1201 		if (strncmp(buf, NDMP_EOM_MAGIC, len) != 0)
1202 			(void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
1203 	} else {
1204 		session->ns_tape.td_pos += n;
1205 		reply->data_in.data_in_len = n;
1206 		reply->data_in.data_in_val = buf;
1207 		reply->error = NDMP_NO_ERR;
1208 		NS_ADD(rtape, n);
1209 	}
1210 }
1211 
1212 
1213 /*
1214  * validmode
1215  *
1216  * Check the tape read mode is valid
1217  */
1218 static boolean_t
1219 validmode(int mode)
1220 {
1221 	boolean_t rv;
1222 
1223 	switch (mode) {
1224 	case NDMP_TAPE_READ_MODE:
1225 	case NDMP_TAPE_WRITE_MODE:
1226 	case NDMP_TAPE_RAW1_MODE:
1227 	case NDMP_TAPE_RAW2_MODE:
1228 		rv = TRUE;
1229 		break;
1230 	default:
1231 		rv = FALSE;
1232 	}
1233 
1234 	return (rv);
1235 }
1236 
1237 
1238 /*
1239  * common_tape_open
1240  *
1241  * Generic function for opening the tape for all versions
1242  *
1243  * Parameters:
1244  *   connection (input) - connection handle.
1245  *   devname (input) - tape device name to open.
1246  *   ndmpmode (input) - mode of opening (read, write, raw)
1247  *
1248  * Returns:
1249  *   void
1250  */
1251 static void
1252 common_tape_open(ndmp_connection_t *connection, char *devname, int ndmpmode)
1253 {
1254 	ndmpd_session_t *session = ndmp_get_client_data(connection);
1255 	char adptnm[SCSI_MAX_NAME];
1256 	int err;
1257 	int mode;
1258 	int sid, lun;
1259 	scsi_adapter_t *sa;
1260 	int devid;
1261 
1262 	err = NDMP_NO_ERR;
1263 
1264 	if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) {
1265 		NDMP_LOG(LOG_INFO,
1266 		    "Connection already has a tape or scsi device open");
1267 		err = NDMP_DEVICE_OPENED_ERR;
1268 	} else if (!validmode(ndmpmode))
1269 		err = NDMP_ILLEGAL_ARGS_ERR;
1270 	if ((sa = scsi_get_adapter(0)) != NULL) {
1271 		NDMP_LOG(LOG_DEBUG, "Adapter device opened: %s", devname);
1272 		(void) strlcpy(adptnm, devname, SCSI_MAX_NAME-2);
1273 		adptnm[SCSI_MAX_NAME-1] = '\0';
1274 		sid = lun = -1;
1275 	}
1276 	if (sa) {
1277 		scsi_find_sid_lun(sa, devname, &sid, &lun);
1278 		if (ndmp_open_list_find(devname, sid, lun) == 0 &&
1279 		    (devid = open(devname, O_RDWR | O_NDELAY)) < 0) {
1280 			NDMP_LOG(LOG_ERR,
1281 			    "Failed to open device %s: %m.", devname);
1282 			err = NDMP_NO_DEVICE_ERR;
1283 		} else {
1284 			(void) close(devid);
1285 		}
1286 	} else {
1287 		NDMP_LOG(LOG_ERR, "%s: No such tape device.", devname);
1288 		err = NDMP_NO_DEVICE_ERR;
1289 	}
1290 
1291 	if (err != NDMP_NO_ERR) {
1292 		tape_open_send_reply(connection, err);
1293 		return;
1294 	}
1295 
1296 	/*
1297 	 * If tape is not opened in raw mode and tape is not loaded
1298 	 * return error.
1299 	 */
1300 	if (ndmpmode != NDMP_TAPE_RAW1_MODE &&
1301 	    ndmpmode != NDMP_TAPE_RAW2_MODE &&
1302 	    !is_tape_unit_ready(adptnm, 0)) {
1303 		tape_open_send_reply(connection, NDMP_NO_TAPE_LOADED_ERR);
1304 		return;
1305 	}
1306 
1307 	mode = (ndmpmode == NDMP_TAPE_READ_MODE) ? O_RDONLY : O_RDWR;
1308 	mode |= O_NDELAY;
1309 	session->ns_tape.td_fd = open(devname, mode);
1310 	if (session->ns_protocol_version == NDMPV4 &&
1311 	    session->ns_tape.td_fd < 0 &&
1312 	    ndmpmode == NDMP_TAPE_RAW_MODE && errno == EACCES) {
1313 		/*
1314 		 * V4 suggests that if the tape is open in raw mode
1315 		 * and could not be opened with write access, it should
1316 		 * be opened read only instead.
1317 		 */
1318 		ndmpmode = NDMP_TAPE_READ_MODE;
1319 		session->ns_tape.td_fd = open(devname, O_RDONLY);
1320 	}
1321 	if (session->ns_tape.td_fd < 0) {
1322 		NDMP_LOG(LOG_ERR, "Failed to open tape device %s: %m.",
1323 		    devname);
1324 		switch (errno) {
1325 		case EACCES:
1326 			err = NDMP_WRITE_PROTECT_ERR;
1327 			break;
1328 		case ENOENT:
1329 			err = NDMP_NO_DEVICE_ERR;
1330 			break;
1331 		case EBUSY:
1332 			err = NDMP_DEVICE_BUSY_ERR;
1333 			break;
1334 		case EPERM:
1335 			err = NDMP_PERMISSION_ERR;
1336 			break;
1337 		default:
1338 			err = NDMP_IO_ERR;
1339 		}
1340 
1341 		tape_open_send_reply(connection, err);
1342 		return;
1343 	}
1344 
1345 	switch (ndmp_open_list_add(connection,
1346 	    adptnm, sid, lun, session->ns_tape.td_fd)) {
1347 	case 0:
1348 		err = NDMP_NO_ERR;
1349 		break;
1350 	case EBUSY:
1351 		err = NDMP_DEVICE_BUSY_ERR;
1352 		break;
1353 	case ENOMEM:
1354 		err = NDMP_NO_MEM_ERR;
1355 		break;
1356 	default:
1357 		err = NDMP_IO_ERR;
1358 	}
1359 	if (err != NDMP_NO_ERR) {
1360 		tape_open_send_reply(connection, err);
1361 		return;
1362 	}
1363 
1364 	session->ns_tape.td_mode = ndmpmode;
1365 	session->ns_tape.td_sid = sid;
1366 	session->ns_tape.td_lun = lun;
1367 	(void) strlcpy(session->ns_tape.td_adapter_name, adptnm, SCSI_MAX_NAME);
1368 	session->ns_tape.td_record_count = 0;
1369 	session->ns_tape.td_eom_seen = FALSE;
1370 
1371 	NDMP_LOG(LOG_DEBUG, "Tape is opened fd: %d", session->ns_tape.td_fd);
1372 
1373 	tape_open_send_reply(connection, NDMP_NO_ERR);
1374 }
1375 
1376 
1377 /*
1378  * common_tape_close
1379  *
1380  * Generic function for closing the tape
1381  *
1382  * Parameters:
1383  *   connection (input) - connection handle.
1384  *
1385  * Returns:
1386  *   void
1387  */
1388 static void
1389 common_tape_close(ndmp_connection_t *connection)
1390 {
1391 	ndmpd_session_t *session = ndmp_get_client_data(connection);
1392 	ndmp_tape_close_reply reply;
1393 
1394 	(void) ndmp_open_list_del(session->ns_tape.td_adapter_name,
1395 	    session->ns_tape.td_sid, session->ns_tape.td_lun);
1396 	(void) close(session->ns_tape.td_fd);
1397 	session->ns_tape.td_fd = -1;
1398 	session->ns_tape.td_sid = 0;
1399 	session->ns_tape.td_lun = 0;
1400 	session->ns_tape.td_write = 0;
1401 	(void) memset(session->ns_tape.td_adapter_name, 0,
1402 	    sizeof (session->ns_tape.td_adapter_name));
1403 	session->ns_tape.td_record_count = 0;
1404 	session->ns_tape.td_eom_seen = FALSE;
1405 
1406 	reply.error = NDMP_NO_ERR;
1407 	ndmp_send_reply(connection, (void *) &reply,
1408 	    "sending tape_close reply");
1409 }
1410 
1411 /*
1412  * tape_open
1413  *
1414  * Will try to open the tape with the given flags and
1415  * path using the given retries and delay intervals
1416  */
1417 int
1418 tape_open(char *path, int flags)
1419 {
1420 	int fd;
1421 	int i = 0;
1422 
1423 	while ((fd = open(path, flags)) == -1 &&
1424 	    i++ < ndmp_tape_open_retries) {
1425 		if (errno != EBUSY)
1426 			break;
1427 		(void) usleep(ndmp_tape_open_delay);
1428 	}
1429 	return (fd);
1430 }
1431