diff options
author | Jan Kryl <jan.kryl@nexenta.com> | 2014-01-20 13:30:27 -0500 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2014-01-22 08:03:54 -0800 |
commit | 9ee94b97c8654689d6a034daec08757fda75d21a (patch) | |
tree | e1510b7fc2dd83e542ca33db99c2d87a53c56e55 /usr/src | |
parent | f7dbdfc7b241e42b135dc9118e41b127cb935483 (diff) | |
download | illumos-gate-9ee94b97c8654689d6a034daec08757fda75d21a.tar.gz |
4496 ndmpd handles wrongly EOM and EOF conditions
Reviewed by: Albert Lee <trisk@nexenta.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/ndmpd/ndmp/ndmpd.h | 8 | ||||
-rw-r--r-- | usr/src/cmd/ndmpd/ndmp/ndmpd_handler.c | 5 | ||||
-rw-r--r-- | usr/src/cmd/ndmpd/ndmp/ndmpd_mover.c | 313 | ||||
-rw-r--r-- | usr/src/cmd/ndmpd/ndmp/ndmpd_tape.c | 357 |
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, |