summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/ndmpd/ndmp/ndmpd.h8
-rw-r--r--usr/src/cmd/ndmpd/ndmp/ndmpd_handler.c5
-rw-r--r--usr/src/cmd/ndmpd/ndmp/ndmpd_mover.c313
-rw-r--r--usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c357
4 files changed, 255 insertions, 428 deletions
diff --git a/usr/src/cmd/ndmpd/ndmp/ndmpd.h b/usr/src/cmd/ndmpd/ndmp/ndmpd.h
index b24c1a58a2..efee818c14 100644
--- a/usr/src/cmd/ndmpd/ndmp/ndmpd.h
+++ b/usr/src/cmd/ndmpd/ndmp/ndmpd.h
@@ -37,6 +37,7 @@
*/
/* Copyright (c) 2007, The Storage Networking Industry Association. */
/* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
+/* Copyright 2014 Nexenta Systems, Inc. All rights reserved. */
#ifndef _NDMPD_H
#define _NDMPD_H
@@ -310,9 +311,6 @@ typedef struct ndmpd_session_tape_desc {
int td_sid;
int td_lun;
char td_adapter_name[SCSI_MAX_NAME];
- ulong_t td_eom_seen:1,
- td_io_err:1,
- td_write:1;
} ndmpd_session_tape_desc_t;
typedef struct ndmpd_session_mover_desc {
@@ -798,7 +796,6 @@ extern int ndmpd_remote_read_v3(ndmpd_session_t *,
char *,
ulong_t);
extern int ndmpd_mover_wait_v3(ndmpd_session_t *);
-extern void ndmpd_write_eom(int);
/*
@@ -1022,6 +1019,9 @@ extern int ndmp_restore_extract_params(ndmpd_session_t *,
extern int ndmp_tar_reader(ndmp_tar_reader_arg_t *);
extern int tape_open(char *, int);
+extern int tape_is_at_bot(ndmpd_session_t *);
+extern int tape_is_at_bof(ndmpd_session_t *);
+extern void fm_dance(ndmpd_session_t *);
extern void ndmp_session_ref(ndmpd_session_t *);
extern void ndmp_session_unref(ndmpd_session_t *);
diff --git a/usr/src/cmd/ndmpd/ndmp/ndmpd_handler.c b/usr/src/cmd/ndmpd/ndmp/ndmpd_handler.c
index 30bafa0f73..2658bb2cef 100644
--- a/usr/src/cmd/ndmpd/ndmp/ndmpd_handler.c
+++ b/usr/src/cmd/ndmpd/ndmp/ndmpd_handler.c
@@ -37,6 +37,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
#include <sys/note.h>
#include "ndmpd.h"
@@ -348,7 +351,7 @@ ndmp_handler_t ndmp_msghdl_tab[] = {
NDMP_TAPE_WRITE,
AUTH_REQUIRED,
{
- HANDL(tape_write, 2, 2),
+ HANDL(tape_write, 3, 2),
HANDL(tape_write, 3, 3),
HANDL(tape_write, 3, 4),
}
diff --git a/usr/src/cmd/ndmpd/ndmp/ndmpd_mover.c b/usr/src/cmd/ndmpd/ndmp/ndmpd_mover.c
index 6bf0a8808b..8772edc199 100644
--- a/usr/src/cmd/ndmpd/ndmp/ndmpd_mover.c
+++ b/usr/src/cmd/ndmpd/ndmp/ndmpd_mover.c
@@ -37,6 +37,7 @@
*/
/* Copyright (c) 2007, The Storage Networking Industry Association. */
/* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
+/* Copyright 2014 Nexenta Systems, Inc. All rights reserved. */
#include <sys/ioctl.h>
#include <sys/types.h>
@@ -62,7 +63,6 @@
static int create_listen_socket_v2(ndmpd_session_t *session, ulong_t *addr,
ushort_t *port);
-static int tape_write(ndmpd_session_t *session, char *data, ssize_t length);
static int tape_read(ndmpd_session_t *session, char *data);
static int change_tape(ndmpd_session_t *session);
static int discard_data(ndmpd_session_t *session, ulong_t length);
@@ -1354,30 +1354,6 @@ ndmpd_mover_connect_v4(ndmp_connection_t *connection, void *body)
*/
/*
- * ndmpd_write_eom
- *
- * Write end-of-media magic string. This is called after hitting the LEOT.
- */
-void
-ndmpd_write_eom(int fd)
-{
- int n;
-
- (void) ndmp_mtioctl(fd, MTWEOF, 1);
- n = write(fd, NDMP_EOM_MAGIC, strlen(NDMP_EOM_MAGIC));
-
- NDMP_LOG(LOG_DEBUG, "%d EOM bytes wrote", n);
- (void) ndmp_mtioctl(fd, MTWEOF, 1);
-
- /*
- * Rewind to the previous file since the last two files are used
- * as the indicator for logical EOM.
- */
- (void) ndmp_mtioctl(fd, MTBSF, 2);
-}
-
-
-/*
* ndmpd_local_write
*
* Writes data to the mover.
@@ -1413,7 +1389,7 @@ ndmpd_local_write(ndmpd_session_t *session, char *data, ulong_t length)
0, session->ns_mover.md_record_size -
session->ns_mover.md_w_index);
- n = tape_write(session, session->ns_mover.md_buf,
+ n = mover_tape_write_v3(session, session->ns_mover.md_buf,
session->ns_mover.md_record_size);
if (n <= 0) {
ndmpd_mover_error(session,
@@ -1439,7 +1415,7 @@ ndmpd_local_write(ndmpd_session_t *session, char *data, ulong_t length)
*/
if (session->ns_mover.md_w_index == 0 &&
length - count >= session->ns_mover.md_record_size) {
- n = tape_write(session, &data[count],
+ n = mover_tape_write_v3(session, &data[count],
session->ns_mover.md_record_size);
if (n <= 0) {
ndmpd_mover_error(session,
@@ -1469,9 +1445,10 @@ ndmpd_local_write(ndmpd_session_t *session, char *data, ulong_t length)
/* Write the buffer if its full */
if (session->ns_mover.md_w_index ==
session->ns_mover.md_record_size) {
- n = tape_write(session, session->ns_mover.md_buf,
+ n = mover_tape_write_v3(session,
+ session->ns_mover.md_buf,
session->ns_mover.md_record_size);
- if (n < 0) {
+ if (n <= 0) {
ndmpd_mover_error(session,
(n == 0 ? NDMP_MOVER_HALT_ABORTED :
NDMP_MOVER_HALT_INTERNAL_ERROR));
@@ -2279,99 +2256,6 @@ accept_connection(void *cookie, int fd, ulong_t mode)
}
/*
- * tape_write
- *
- * Writes a data record to tape. Detects and handles EOT conditions.
- *
- * Parameters:
- * session (input) - session pointer.
- * data (input) - data to be written.
- * length (input) - length of data to be written.
- *
- * Returns:
- * 0 - operation aborted by client.
- * -1 - error.
- * otherwise - number of bytes written.
- */
-static int
-tape_write(ndmpd_session_t *session, char *data, ssize_t length)
-{
- ssize_t n;
- int err;
-
- for (; ; ) {
- /*
- * Refer to the comment at the top of ndmpd_tape.c file for
- * Mammoth2 tape drives.
- */
- if (session->ns_tape.td_eom_seen) {
- NDMP_LOG(LOG_DEBUG, "eom_seen");
- session->ns_tape.td_eom_seen = FALSE;
- /*
- * End of media reached.
- * Notify client and wait for the client to
- * either abort the operation or continue the
- * operation after changing the tape.
- */
- NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
- ++ndmp_log_msg_id,
- "End of tape reached. Load next tape.\n");
-
- err = change_tape(session);
-
- /* Operation aborted or connection terminated? */
- if (err < 0)
- return (-1);
-
- continue;
- }
-
- n = write(session->ns_tape.td_fd, data, length);
- if (n < 0) {
- NDMP_LOG(LOG_ERR, "Tape write error: %m.");
- return (-1);
- }
- NS_ADD(wtape, n);
-
- if (n == 0 || n != length) {
- if (n != 0) {
- NDMP_LOG(LOG_DEBUG, "LEOT n: %d", n);
-
- NDMP_LOG(LOG_DEBUG, "Backup one record");
- (void) ndmp_mtioctl(session->ns_tape.td_fd,
- MTBSR, 1);
-
- /* setting logical EOM */
- ndmpd_write_eom(session->ns_tape.td_fd);
- }
-
- /*
- * End of media reached.
- * Notify client and wait for the client to
- * either abort the operation or continue the
- * operation after changing the tape.
- */
- NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
- ++ndmp_log_msg_id,
- "End of tape reached. Load next tape.\n");
-
- err = change_tape(session);
-
- /* Operation aborted or connection terminated? */
- if (err < 0)
- return (-1);
-
- /* Retry the write to the new tape. */
- continue;
- }
-
- session->ns_tape.td_record_count++;
- return (n);
- }
-}
-
-
-/*
* tape_read
*
* Reads a data record from tape. Detects and handles EOT conditions.
@@ -3143,7 +3027,8 @@ mover_tape_write_one_buf(ndmpd_session_t *session, tlm_buffer_t *buf)
buf->tb_full, buf->tb_eot, buf->tb_eof, buf->tb_errno,
buf->tb_buffer_size, buf->tb_buffer_data);
- n = tape_write(session, buf->tb_buffer_data, buf->tb_buffer_size);
+ n = mover_tape_write_v3(session, buf->tb_buffer_data,
+ buf->tb_buffer_size);
NDMP_LOG(LOG_DEBUG, "n: %d", n);
@@ -3668,37 +3553,9 @@ static int
mover_tape_write_v3(ndmpd_session_t *session, char *data, ssize_t length)
{
ssize_t n;
- int err;
-
- for (; ; ) {
- /*
- * Refer to the comment at the top of ndmpd_tape.c file for
- * Mammoth2 tape drives.
- */
- if (session->ns_tape.td_eom_seen) {
- NDMP_LOG(LOG_DEBUG, "eom_seen");
-
- session->ns_tape.td_eom_seen = FALSE;
- /*
- * End of media reached.
- * Notify client and wait for the client to
- * either abort the operation or continue the
- * operation after changing the tape.
- */
- NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
- ++ndmp_log_msg_id,
- "End of tape reached. Load next tape");
-
- err = mover_pause_v3(session, NDMP_MOVER_PAUSE_EOM);
-
- /* Operation aborted or connection terminated? */
- if (err < 0)
- return (-1);
-
- /* Retry the write to the new tape. */
- continue;
- }
+ ssize_t count = length;
+ while (count > 0) {
/*
* Enforce mover window on write.
*/
@@ -3707,57 +3564,49 @@ mover_tape_write_v3(ndmpd_session_t *session, char *data, ssize_t length)
session->ns_mover.md_window_length) {
NDMP_LOG(LOG_DEBUG, "MOVER_PAUSE_EOW");
- err = mover_pause_v3(session, NDMP_MOVER_PAUSE_EOW);
- /* Operation aborted or connection terminated? */
- if (err < 0)
+ if (mover_pause_v3(session, NDMP_MOVER_PAUSE_EOW) < 0)
+ /* Operation aborted or connection terminated */
return (-1);
}
- n = write(session->ns_tape.td_fd, data, length);
+ n = write(session->ns_tape.td_fd, data, count);
if (n < 0) {
NDMP_LOG(LOG_ERR, "Tape write error: %m.");
return (-1);
+ } else if (n > 0) {
+ NS_ADD(wtape, n);
+ count -= n;
+ data += n;
+ session->ns_tape.td_record_count++;
}
- NS_ADD(wtape, n);
- if (n == 0 || n != length) {
- if (n != 0) {
- /*
- * Backup one record since the record
- * hits the EOM.
- */
- NDMP_LOG(LOG_DEBUG, "Back up one record");
- (void) ndmp_mtioctl(session->ns_tape.td_fd,
- MTBSR, 1);
+ /* EOM handling */
+ if (count > 0) {
+ struct mtget mtstatus;
- /* setting logical EOM */
- ndmpd_write_eom(session->ns_tape.td_fd);
- }
+ (void) ioctl(session->ns_tape.td_fd, MTIOCGET,
+ &mtstatus);
+ NDMP_LOG(LOG_DEBUG, "EOM detected (%d written bytes, "
+ "mover record %d, file #%d, block #%d)", n,
+ session->ns_tape.td_record_count,
+ mtstatus.mt_fileno, mtstatus.mt_blkno);
/*
- * End of media reached.
- * Notify client and wait for the client to
- * either abort the operation or continue the
- * operation after changing the tape.
+ * Notify the client to either abort the operation
+ * or change the tape.
*/
NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
++ndmp_log_msg_id,
"End of tape reached. Load next tape");
- err = mover_pause_v3(session, NDMP_MOVER_PAUSE_EOM);
-
- /* Operation aborted or connection terminated? */
- if (err < 0)
+ if (mover_pause_v3(session, NDMP_MOVER_PAUSE_EOM) < 0)
+ /* Operation aborted or connection terminated */
return (-1);
-
- /* Retry the write to the new tape. */
- continue;
}
-
- session->ns_tape.td_record_count++;
- return (n);
}
+
+ return (length);
}
@@ -3904,7 +3753,7 @@ ndmpd_local_write_v3(ndmpd_session_t *session, char *data, ulong_t length)
n = mover_tape_write_v3(session,
session->ns_mover.md_buf,
session->ns_mover.md_record_size);
- if (n < 0) {
+ if (n <= 0) {
ndmpd_mover_error(session,
(n == 0 ? NDMP_MOVER_HALT_ABORTED :
NDMP_MOVER_HALT_MEDIA_ERROR));
@@ -4017,59 +3866,84 @@ mover_data_read_v3(void *cookie, int fd, ulong_t mode)
static int
mover_tape_read_v3(ndmpd_session_t *session, char *data)
{
+ int pause_reason;
ssize_t n;
int err;
int count;
count = session->ns_mover.md_record_size;
- for (; ; ) {
+ while (count > 0) {
+ pause_reason = NDMP_MOVER_PAUSE_NA;
+
n = read(session->ns_tape.td_fd, data, count);
if (n < 0) {
- NDMP_LOG(LOG_ERR, "Tape read error: %m.");
- return (TAPE_READ_ERR);
- }
- NS_ADD(rtape, n);
-
- if (n == 0) {
+ /*
+ * If at beginning of file and read fails with EIO,
+ * then it's repeated attempt to read at EOT.
+ */
+ if (errno == EIO && tape_is_at_bof(session)) {
+ NDMP_LOG(LOG_DEBUG, "Repeated read at EOT");
+ pause_reason = NDMP_MOVER_PAUSE_EOM;
+ NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
+ ++ndmp_log_msg_id,
+ "End of tape reached. Load next tape");
+ }
+ /*
+ * According to NDMPv4 spec preferred error code when
+ * trying to read from blank tape is NDMP_EOM_ERR.
+ */
+ else if (errno == EIO && tape_is_at_bot(session)) {
+ NDMP_LOG(LOG_ERR,
+ "Blank tape detected, returning EOM");
+ NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
+ ++ndmp_log_msg_id,
+ "Blank tape. Load another tape");
+ pause_reason = NDMP_MOVER_PAUSE_EOM;
+ } else {
+ NDMP_LOG(LOG_ERR, "Tape read error: %m.");
+ return (TAPE_READ_ERR);
+ }
+ } else if (n > 0) {
+ NS_ADD(rtape, n);
+ data += n;
+ count -= n;
+ session->ns_tape.td_record_count++;
+ } else {
if (!is_writer_running_v3(session))
return (TAPE_NO_WRITER_ERR);
/*
- * End of media reached.
- * Notify client and wait for the client to
- * either abort the data operation or continue the
- * operation after changing the tape.
+ * End of file or media reached. Notify client and
+ * wait for the client to either abort the data
+ * operation or continue the operation after changing
+ * the tape.
*/
- NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
- ++ndmp_log_msg_id,
- "End of tape reached. Load next tape");
+ if (tape_is_at_bof(session)) {
+ NDMP_LOG(LOG_DEBUG, "EOT detected");
+ pause_reason = NDMP_MOVER_PAUSE_EOM;
+ NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
+ ++ndmp_log_msg_id, "End of medium reached");
+ } else {
+ NDMP_LOG(LOG_DEBUG, "EOF detected");
+ /* reposition the tape to BOT side of FM */
+ fm_dance(session);
+ pause_reason = NDMP_MOVER_PAUSE_EOF;
+ NDMP_APILOG((void*)session, NDMP_LOG_NORMAL,
+ ++ndmp_log_msg_id, "End of file reached.");
+ }
+ }
- err = mover_pause_v3(session, NDMP_MOVER_PAUSE_EOF);
+ if (pause_reason != NDMP_MOVER_PAUSE_NA) {
+ err = mover_pause_v3(session, pause_reason);
/* Operation aborted or connection terminated? */
if (err < 0) {
- /*
- * Back up one record if it's read but not
- * used.
- */
- if (count != session->ns_mover.md_record_size)
- (void) ndmp_mtioctl(
- session->ns_tape.td_fd, MTBSR, 1);
return (0);
}
-
- /* Retry the read from the new tape. */
- continue;
- }
-
- data += n;
- count -= n;
- if (count <= 0) {
- session->ns_mover.md_record_num++;
- session->ns_tape.td_record_count++;
- return (n);
+ /* Retry the read from new location */
}
}
+ return (session->ns_mover.md_record_size);
}
@@ -4168,6 +4042,7 @@ mover_data_write_v3(void *cookie, int fd, ulong_t mode)
}
session->ns_mover.md_w_index = n;
+ session->ns_mover.md_record_num++;
}
/*
@@ -4540,6 +4415,7 @@ ndmpd_local_read_v3(ndmpd_session_t *session, char *data, ulong_t length)
count += n;
session->ns_mover.md_bytes_left_to_read -= n;
session->ns_mover.md_position += n;
+ session->ns_mover.md_record_num++;
continue;
}
@@ -4557,6 +4433,7 @@ ndmpd_local_read_v3(ndmpd_session_t *session, char *data, ulong_t length)
session->ns_mover.md_w_index = n;
session->ns_mover.md_r_index = 0;
+ session->ns_mover.md_record_num++;
NDMP_LOG(LOG_DEBUG, "n: %d", n);
diff --git a/usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c b/usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c
index 8dd46411f9..0d5194c27e 100644
--- a/usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c
+++ b/usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c
@@ -2,7 +2,6 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
/*
* BSD 3 Clause License
*
@@ -38,6 +37,7 @@
*/
/* Copyright (c) 2007, The Storage Networking Industry Association. */
/* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
+/* Copyright 2014 Nexenta Systems, Inc. All rights reserved. */
#include <sys/param.h>
#include <fcntl.h>
@@ -65,6 +65,29 @@ int ndmp_tape_open_retries = 5;
int ndmp_tape_open_delay = 1000;
/*
+ * A few words about EOT (end-of-tape) and EOM handling on tapes with SVR4
+ * semantic:
+ *
+ * We adhere to terminology as used in st driver. EOT means end of recorded
+ * data on a tape. This is different from EOM (somewhere referred to as LEOT)
+ * which is the end of tape medium. EOT is meaningful only for reads while EOM
+ * is meaningful only for writes. It's not possible to read after EOT (fails
+ * with EIO), but it's possible to write data after EOM. EOM returned by st
+ * driver on modern tape drives is just indication that the physical end of
+ * tape medium is nearing and that writer should write just the necessary
+ * minimum and stop writing. When physical end of tape is reached all writes
+ * return EIO. If EOM is crossed during read operation then st driver doesn't
+ * bother to report it to client and that's alright because reads don't care
+ * where medium physically ends but they care about meaningful data recorded on
+ * the tape and as long as there are such data reads should continue to work.
+ *
+ * When reading EOT is signalled by st driver by two empty consecutive reads
+ * (with FSF done between them). When writing EOM is signalled by empty write
+ * (a write which writes zero bytes). Following writes succeed until physical
+ * end of tape is reached in which case EIO is returned.
+ */
+
+/*
* ************************************************************************
* NDMP V2 HANDLERS
* ************************************************************************
@@ -196,7 +219,6 @@ ndmpd_tape_open_v2(ndmp_connection_t *connection, void *body)
session->ns_tape.td_lun = lun;
(void) strlcpy(session->ns_tape.td_adapter_name, adptnm, SCSI_MAX_NAME);
session->ns_tape.td_record_count = 0;
- session->ns_tape.td_eom_seen = FALSE;
NDMP_LOG(LOG_DEBUG, "Tape is opened fd: %d", session->ns_tape.td_fd);
@@ -389,6 +411,7 @@ ndmpd_tape_mtio_v2(ndmp_connection_t *connection, void *body)
do {
NS_UPD(twait, trun);
+ errno = 0;
rc = ioctl(session->ns_tape.td_fd, MTIOCTOP, &tapeop);
NS_UPD(trun, twait);
NDMP_LOG(LOG_DEBUG,
@@ -445,104 +468,6 @@ ndmpd_tape_mtio_v2(ndmp_connection_t *connection, void *body)
/*
- * ndmpd_tape_write_v2
- *
- * This handler handles tape_write requests.
- * This interface is a non-buffered interface. Each write request
- * maps directly to a write to the tape device. It is the responsibility
- * of the NDMP client to pad the data to the desired record size.
- * It is the responsibility of the NDMP client to ensure that the
- * length is a multiple of the tape block size if the tape device
- * is in fixed block mode.
- *
- * Parameters:
- * connection (input) - connection handle.
- * body (input) - request message body.
- *
- * Returns:
- * void
- */
-void
-ndmpd_tape_write_v2(ndmp_connection_t *connection, void *body)
-{
- ndmp_tape_write_request *request = (ndmp_tape_write_request *) body;
- ndmp_tape_write_reply reply;
- ndmpd_session_t *session = ndmp_get_client_data(connection);
- ssize_t n;
-
- reply.count = 0;
-
- if (session->ns_tape.td_fd == -1) {
- NDMP_LOG(LOG_ERR, "Tape device is not open.");
- reply.error = NDMP_DEV_NOT_OPEN_ERR;
- ndmp_send_reply(connection, (void *) &reply,
- "sending tape_write reply");
- return;
- }
- if (session->ns_tape.td_mode == NDMP_TAPE_READ_MODE) {
- NDMP_LOG(LOG_INFO, "Tape device opened in read-only mode");
- reply.error = NDMP_PERMISSION_ERR;
- ndmp_send_reply(connection, (void *) &reply,
- "sending tape_write reply");
- return;
- }
- if (request->data_out.data_out_len == 0) {
- reply.error = NDMP_NO_ERR;
- ndmp_send_reply(connection, (void *) &reply,
- "sending tape_write reply");
- return;
- }
-
- if (session->ns_tape.td_eom_seen) {
- /*
- * Refer to the comment at the top of this file for
- * Mammoth2 tape drives.
- */
- NDMP_LOG(LOG_DEBUG, "eom_seen");
- ndmpd_write_eom(session->ns_tape.td_fd);
-
- session->ns_tape.td_eom_seen = FALSE;
- reply.error = NDMP_EOM_ERR;
- ndmp_send_reply(connection, (void *) &reply,
- "sending tape_write reply");
- return;
- }
-
- n = write(session->ns_tape.td_fd, request->data_out.data_out_val,
- request->data_out.data_out_len);
- if (n >= 0) {
- session->ns_tape.td_write = 1;
- NS_ADD(wtape, n);
- }
- if (n == 0) {
- NDMP_LOG(LOG_DEBUG, "n == 0");
- reply.error = NDMP_EOM_ERR;
- session->ns_tape.td_eom_seen = FALSE;
- } else if (n < 0) {
- NDMP_LOG(LOG_ERR, "Tape write error: %m.");
- reply.error = NDMP_IO_ERR;
- } else {
- reply.count = n;
- reply.error = NDMP_NO_ERR;
-
- /*
- * a logical end of tape will return number of bytes written
- * less than rquested, and one more request to write will
- * give 0, and then no-space
- */
- if (n < request->data_out.data_out_len) {
- NDMP_LOG(LOG_DEBUG, "LEOT: n: %d", n);
- session->ns_tape.td_eom_seen = TRUE;
- } else {
- session->ns_tape.td_eom_seen = FALSE;
- }
- }
- ndmp_send_reply(connection, &reply,
- "sending tape_write reply");
-}
-
-
-/*
* ndmpd_tape_read_v2
*
* This handler handles tape_read requests.
@@ -593,8 +518,6 @@ ndmpd_tape_read_v2(ndmp_connection_t *connection, void *body)
return;
}
- session->ns_tape.td_eom_seen = FALSE;
-
unbuffered_read(session, buf, request->count, &reply);
ndmp_send_reply(connection, (void *) &reply, "sending tape_read reply");
@@ -631,7 +554,6 @@ ndmpd_tape_execute_cdb_v2(ndmp_connection_t *connection, void *body)
ndmp_send_reply(connection, (void *) &reply,
"sending tape_execute_cdb reply");
} else {
- session->ns_tape.td_eom_seen = FALSE;
ndmp_execute_cdb(session, session->ns_tape.td_adapter_name,
session->ns_tape.td_sid, session->ns_tape.td_lun,
(ndmp_execute_cdb_request *)request);
@@ -752,32 +674,92 @@ ndmpd_tape_get_state_v3(ndmp_connection_t *connection, void *body)
"sending tape_get_state reply");
}
+/*
+ * tape_is_at_bot
+ *
+ * Returns 1 if tape is at BOT, 0 on error or not at BOT.
+ *
+ */
+int
+tape_is_at_bot(ndmpd_session_t *session)
+{
+ struct mtget mtstatus;
+
+ if (ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == 0 &&
+ mtstatus.mt_fileno == 0 && mtstatus.mt_blkno == 0)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * If we are at the beginning of a file (block # is zero) and read returns
+ * zero bytes then this has to be end of recorded data on the tape. Repeated
+ * reads at EOT return EIO. In both cases (zero read and EIO read) this
+ * function should be used to test if we are at EOT.
+ *
+ * Returns 1 if tape is at BOF, 0 on error or not at BOF.
+ */
+int
+tape_is_at_bof(ndmpd_session_t *session)
+{
+ struct mtget mtstatus;
+
+ if ((ioctl(session->ns_tape.td_fd, MTIOCGET, &mtstatus) == 0) &&
+ (mtstatus.mt_fileno > 0) && (mtstatus.mt_blkno == 0))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Skips forward over a file mark and then back before the file mark. Why is
+ * this needed? There are two reasons for it:
+ *
+ * 1) Because NDMPv4 spec requires that when EOF is encountered, the tape
+ * position should remain on BOT side of the file mark. When st driver reaches
+ * end of file get-position mtioctl reports position before file mark, however
+ * the file mark has already been read and the real position is thus after the
+ * file mark (real position as reported for example by uscsi commands). Thus we
+ * need to do FSF, which does nothing but only updates file & block counter in
+ * st driver and then BSF, which sets the position before the file mark. Thus
+ * current position as reported by scsi and mtioctl will be in sync.
+ *
+ * 2) st driver returns EIO for repeated reads at EOF while according to NDMP
+ * spec we should continue to return zero bytes until FSF is done. By skipping
+ * forward and backward, st driver will return zero bytes for the next read
+ * again and we don't need to specifically handle this case.
+ */
+void
+fm_dance(ndmpd_session_t *session)
+{
+ (void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
+ (void) ndmp_mtioctl(session->ns_tape.td_fd, MTBSF, 1);
+}
/*
* ndmpd_tape_write_v3
*
- * This handler handles tape_write requests.
- * This interface is a non-buffered interface. Each write request
- * maps directly to a write to the tape device. It is the responsibility
- * of the NDMP client to pad the data to the desired record size.
- * It is the responsibility of the NDMP client to ensure that the
- * length is a multiple of the tape block size if the tape device
- * is in fixed block mode.
+ * This handler handles tape_write requests. This interface is a non-buffered
+ * interface. Each write request maps directly to a write to the tape device.
+ * It is the responsibility of the NDMP client to pad the data to the desired
+ * record size. It is the responsibility of the NDMP client to ensure that the
+ * length is a multiple of the tape block size if the tape device is in fixed
+ * block mode.
+ *
+ * A logical end of tape will return number of bytes written less than
+ * requested, and one more request to write will give 0 and NDMP_EOM_ERR,
+ * followed by NDMP_NO_ERR until NDMP_IO_ERR when physical end of tape is
+ * reached.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
- *
- * Returns:
- * void
*/
-void
-ndmpd_tape_write_v3(ndmp_connection_t *connection, void *body)
-{
- ndmp_tape_write_request *request = (ndmp_tape_write_request *) body;
- ndmp_tape_write_reply reply;
- ndmpd_session_t *session = ndmp_get_client_data(connection);
- ssize_t n;
+void ndmpd_tape_write_v3(ndmp_connection_t *connection, void *body) {
+ ndmp_tape_write_request *request = (ndmp_tape_write_request *)body;
+ ndmp_tape_write_reply reply; ndmpd_session_t *session =
+ ndmp_get_client_data(connection); ssize_t n;
reply.count = 0;
@@ -816,65 +798,48 @@ ndmpd_tape_write_v3(ndmp_connection_t *connection, void *body)
return;
}
- /*
- * Refer to the comment at the top of this file for
- * Mammoth2 tape drives.
- */
- if (session->ns_tape.td_eom_seen) {
- NDMP_LOG(LOG_DEBUG, "eom_seen");
- ndmpd_write_eom(session->ns_tape.td_fd);
- session->ns_tape.td_eom_seen = FALSE;
- reply.error = NDMP_EOM_ERR;
- ndmp_send_reply(connection, (void *) &reply,
- "sending tape_write reply");
- return;
- }
-
n = write(session->ns_tape.td_fd, request->data_out.data_out_val,
request->data_out.data_out_len);
- session->ns_tape.td_eom_seen = FALSE;
- if (n >= 0) {
- session->ns_tape.td_write = 1;
- NS_ADD(wtape, n);
- }
- if (n == 0) {
- NDMP_LOG(LOG_INFO, "EOM detected");
- reply.error = NDMP_EOM_ERR;
- session->ns_tape.td_eom_seen = TRUE;
- } else if (n < 0) {
+ if (n < 0) {
NDMP_LOG(LOG_ERR, "Tape write error: %m.");
reply.error = NDMP_IO_ERR;
+ } else if (n == 0) {
+ NDMP_LOG(LOG_INFO, "EOM detected");
+ reply.error = NDMP_EOM_ERR;
} else {
+ NS_ADD(wtape, n);
reply.count = n;
reply.error = NDMP_NO_ERR;
+
+ if (n < request->data_out.data_out_len)
+ NDMP_LOG(LOG_DEBUG,
+ "EOM is coming (partial write of %d bytes)", n);
}
ndmp_send_reply(connection, (void *) &reply,
"sending tape_write reply");
}
-
/*
* ndmpd_tape_read_v3
*
- * This handler handles tape_read requests.
- * This interface is a non-buffered interface. Each read request
- * maps directly to a read to the tape device. It is the responsibility
- * of the NDMP client to issue read requests with a length that is at
- * least as large as the record size used write the tape. The tape driver
- * always reads a full record. Data is discarded if the read request is
- * smaller than the record size.
- * It is the responsibility of the NDMP client to ensure that the
- * length is a multiple of the tape block size if the tape device
- * is in fixed block mode.
+ * This handler handles tape_read requests. This interface is a non-buffered
+ * interface. Each read request maps directly to a read to the tape device. It
+ * is the responsibility of the NDMP client to issue read requests with a
+ * length that is at least as large as the record size used write the tape. The
+ * tape driver always reads a full record. Data is discarded if the read
+ * request is smaller than the record size. It is the responsibility of the
+ * NDMP client to ensure that the length is a multiple of the tape block size
+ * if the tape device is in fixed block mode.
+ *
+ * A logical end of tape will return less bytes than requested, and one more
+ * request to read will give 0 and NDMP_EOM_ERR. All subsequent reads will
+ * return NDMP_EOM_ERR until the tape is repositioned.
*
* Parameters:
* connection (input) - connection handle.
* body (input) - request message body.
- *
- * Returns:
- * void
*/
void
ndmpd_tape_read_v3(ndmp_connection_t *connection, void *body)
@@ -883,7 +848,7 @@ ndmpd_tape_read_v3(ndmp_connection_t *connection, void *body)
ndmp_tape_read_reply reply;
ndmpd_session_t *session = ndmp_get_client_data(connection);
char *buf;
- int n, len;
+ int n;
reply.data_in.data_in_len = 0;
@@ -921,7 +886,6 @@ ndmpd_tape_read_v3(ndmp_connection_t *connection, void *body)
"sending tape_read reply");
return;
}
- session->ns_tape.td_eom_seen = FALSE;
n = read(session->ns_tape.td_fd, buf, request->count);
if (n < 0) {
@@ -931,51 +895,41 @@ ndmpd_tape_read_v3(ndmp_connection_t *connection, void *body)
*/
if (errno == ENOSPC) {
reply.error = NDMP_EOF_ERR;
+ }
+ /*
+ * If at beginning of file and read fails with EIO, then it's
+ * repeated attempt to read at EOT.
+ */
+ else if (errno == EIO && tape_is_at_bof(session)) {
+ NDMP_LOG(LOG_DEBUG, "Repeated read at EOT");
+ reply.error = NDMP_EOM_ERR;
+ }
+ /*
+ * According to NDMPv4 spec preferred error code when
+ * trying to read from blank tape is NDMP_EOM_ERR.
+ */
+ else if (errno == EIO && tape_is_at_bot(session)) {
+ NDMP_LOG(LOG_ERR, "Blank tape detected, returning EOM");
+ reply.error = NDMP_EOM_ERR;
} else {
NDMP_LOG(LOG_ERR, "Tape read error: %m.");
reply.error = NDMP_IO_ERR;
}
} else if (n == 0) {
- (void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
-
- len = strlen(NDMP_EOM_MAGIC);
- (void) memset(buf, 0, len);
- n = read(session->ns_tape.td_fd, buf, len);
- buf[len] = '\0';
-
- NDMP_LOG(LOG_DEBUG, "Checking EOM: nread %d [%s]", n, buf);
-
- if (strncmp(buf, NDMP_EOM_MAGIC, len) == 0) {
+ if (tape_is_at_bof(session)) {
+ NDMP_LOG(LOG_DEBUG, "EOT detected");
reply.error = NDMP_EOM_ERR;
- NDMP_LOG(LOG_DEBUG, "NDMP_EOM_ERR");
} else {
+ /* reposition the tape to BOT side of FM */
+ fm_dance(session);
+ NDMP_LOG(LOG_DEBUG, "EOF detected");
reply.error = NDMP_EOF_ERR;
- NDMP_LOG(LOG_DEBUG, "NDMP_EOF_ERR");
}
- if (n > 0)
- (void) ndmp_mtioctl(session->ns_tape.td_fd, MTBSR, 1);
} else {
- /*
- * Symantec fix for import phase
- *
- * As import process from symantec skips filemarks
- * they can come across to NDMP_EOM_MAGIC and treat
- * it as data. This fix prevents the magic to be
- * sent to the client and the read will return zero bytes
- * and set the NDMP_EOM_ERR error. The tape should
- * be positioned at the EOT side of the file mark.
- */
- len = strlen(NDMP_EOM_MAGIC);
- if (n == len && strncmp(buf, NDMP_EOM_MAGIC, len) == 0) {
- reply.error = NDMP_EOM_ERR;
- (void) ndmp_mtioctl(session->ns_tape.td_fd, MTFSF, 1);
- NDMP_LOG(LOG_DEBUG, "NDMP_EOM_ERR");
- } else {
- session->ns_tape.td_pos += n;
- reply.data_in.data_in_len = n;
- reply.data_in.data_in_val = buf;
- reply.error = NDMP_NO_ERR;
- }
+ session->ns_tape.td_pos += n;
+ reply.data_in.data_in_len = n;
+ reply.data_in.data_in_val = buf;
+ reply.error = NDMP_NO_ERR;
NS_ADD(rtape, n);
}
@@ -1058,21 +1012,17 @@ ndmpd_tape_get_state_v4(ndmp_connection_t *connection, void *body)
if (dtp.bsize == 0)
reply.blockno = mtstatus.mt_blkno;
else
- reply.blockno = mtstatus.mt_blkno *
+ reply.blockno = mtstatus.mt_blkno /
(session->ns_mover.md_record_size / dtp.bsize);
- reply.total_space = long_long_to_quad(0); /* not supported */
- reply.space_remain = long_long_to_quad(0); /* not supported */
-
+ reply.total_space = long_long_to_quad(0LL); /* not supported */
+ reply.space_remain = long_long_to_quad(0LL); /* not supported */
reply.soft_errors = 0;
- reply.total_space = long_long_to_quad(0LL);
- reply.space_remain = long_long_to_quad(0LL);
reply.unsupported = NDMP_TAPE_STATE_SOFT_ERRORS_INVALID |
NDMP_TAPE_STATE_TOTAL_SPACE_INVALID |
NDMP_TAPE_STATE_SPACE_REMAIN_INVALID |
NDMP_TAPE_STATE_PARTITION_INVALID;
-
NDMP_LOG(LOG_DEBUG, "f 0x%x, fnum %d, bsize %d, bno: %d",
reply.flags, reply.file_num, reply.block_size, reply.blockno);
@@ -1366,7 +1316,6 @@ common_tape_open(ndmp_connection_t *connection, char *devname, int ndmpmode)
session->ns_tape.td_lun = lun;
(void) strlcpy(session->ns_tape.td_adapter_name, adptnm, SCSI_MAX_NAME);
session->ns_tape.td_record_count = 0;
- session->ns_tape.td_eom_seen = FALSE;
NDMP_LOG(LOG_DEBUG, "Tape is opened fd: %d", session->ns_tape.td_fd);
@@ -1397,11 +1346,9 @@ common_tape_close(ndmp_connection_t *connection)
session->ns_tape.td_fd = -1;
session->ns_tape.td_sid = 0;
session->ns_tape.td_lun = 0;
- session->ns_tape.td_write = 0;
(void) memset(session->ns_tape.td_adapter_name, 0,
sizeof (session->ns_tape.td_adapter_name));
session->ns_tape.td_record_count = 0;
- session->ns_tape.td_eom_seen = FALSE;
reply.error = NDMP_NO_ERR;
ndmp_send_reply(connection, (void *) &reply,