/*
- * Copyright (c) 2005-2008 Zmanda Inc. All Rights Reserved.
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License version 2.1 as
- * published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful, but
+ * Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
- * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
+ * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
*/
+#include "amanda.h"
+#include "pipespawn.h"
#include <string.h> /* memset() */
#include "util.h"
-#include "tape-device.h"
-#include "tape-ops.h"
+#include "device.h"
-/* This is equal to 2*1024*1024*1024 - 16*1024*1024 - 1, but written
+#ifdef HAVE_SYS_TAPE_H
+# include <sys/tape.h>
+#endif
+#ifdef HAVE_SYS_MTIO_H
+# include <sys/mtio.h>
+#endif
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+/* This is equal to 2*1024*1024*1024 - 16*1024*1024 - 1, but written
explicitly to avoid overflow issues. */
#define RESETOFS_THRESHOLD (0x7effffff)
/* Largest possible block size on SCSI systems. */
#define LARGEST_BLOCK_ESTIMATE (16 * 1024 * 1024)
+/*
+ * Type checking and casting macros
+ */
+
+#define TYPE_TAPE_DEVICE (tape_device_get_type())
+#define TAPE_DEVICE(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), tape_device_get_type(), TapeDevice)
+#define TAPE_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), tape_device_get_type(), TapeDevice const)
+#define TAPE_DEVICE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), tape_device_get_type(), TapeDeviceClass)
+#define IS_TAPE_DEVICE(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), tape_device_get_type ())
+#define TAPE_DEVICE_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), tape_device_get_type(), TapeDeviceClass)
+GType tape_device_get_type (void);
+
+/*
+ * Main object structure
+ */
+typedef struct TapeDevicePrivate_s TapeDevicePrivate;
+typedef struct _TapeDevice {
+ Device __parent__;
+
+ /* It should go without saying that all this stuff is
+ * look-but-don't-touch. */
+
+ /* characteristics of the device */
+ gboolean fsf, bsf, fsr, bsr, eom, bsf_after_eom, broken_gmt_online;
+ gboolean leom;
+ gboolean nonblocking_open, fsf_after_filemark;
+ int final_filemarks;
+
+ /* 0 if we opened with O_RDWR; error otherwise. */
+ gboolean write_open_errno;
+ int fd;
+
+ TapeDevicePrivate * private;
+} TapeDevice;
+
struct TapeDevicePrivate_s {
/* This holds the total number of bytes written to the device,
modulus RESETOFS_THRESHOLD. */
int write_count;
char * device_filename;
- gsize read_buffer_size;
+ gsize read_block_size;
+};
+
+/*
+ * Class definition
+ */
+
+typedef struct _TapeDeviceClass TapeDeviceClass;
+struct _TapeDeviceClass {
+ DeviceClass __parent__;
};
+void tape_device_register(void);
+
+/* useful callback for tape ops */
+void tape_device_set_capabilities(TapeDevice *self,
+ gboolean fsf, PropertySurety fsf_surety, PropertySource fsf_source,
+ gboolean fsf_after_filemark, PropertySurety faf_surety, PropertySource faf_source,
+ gboolean bsf, PropertySurety bsf_surety, PropertySource bsf_source,
+ gboolean fsr, PropertySurety fsr_surety, PropertySource fsr_source,
+ gboolean bsr, PropertySurety bsr_surety, PropertySource bsr_source,
+ gboolean eom, PropertySurety eom_surety, PropertySource eom_source,
+ gboolean leom, PropertySurety leom_surety, PropertySource leom_source,
+ gboolean bsf_after_eom, PropertySurety bae_surety, PropertySource bae_source,
+ guint final_filemarks, PropertySurety ff_surety, PropertySource ff_source);
+
+/* Real Operations (always return FALSE if not implemented) */
+gboolean tape_rewind(int fd);
+gboolean tape_fsf(int fd, guint count);
+gboolean tape_bsf(int fd, guint count);
+gboolean tape_fsr(int fd, guint count);
+gboolean tape_bsr(int fd, guint count);
+gint tape_fileno(int fd);
+
+/* tape_fileno returns tape position file number, or one of these: */
+#define TAPE_OP_ERROR -1
+#define TAPE_POSITION_UNKNOWN -2
+
/* Possible (abstracted) results from a system I/O operation. */
typedef enum {
RESULT_SUCCESS,
- RESULT_ERROR, /* Undefined error. */
+ RESULT_ERROR, /* Undefined error (*errmsg set) */
RESULT_SMALL_BUFFER, /* Tried to read with a buffer that is too
small. */
RESULT_NO_DATA, /* End of File, while reading */
RESULT_MAX
} IoResult;
-/*
- * Our device-specific properties. These are not static because they are
- * accessed from the OS-specific tape-*.c files.
- */
-DevicePropertyBase device_property_broken_gmt_online;
-DevicePropertyBase device_property_fsf;
-DevicePropertyBase device_property_fsf_after_filemark;
-DevicePropertyBase device_property_bsf;
-DevicePropertyBase device_property_fsr;
-DevicePropertyBase device_property_bsr;
-DevicePropertyBase device_property_eom;
-DevicePropertyBase device_property_bsf_after_eom;
-DevicePropertyBase device_property_nonblocking_open;
-DevicePropertyBase device_property_final_filemarks;
+/* returns a fileno like tape_fileno */
+gint tape_eod(int fd);
-void tape_device_register(void);
+gboolean tape_weof(int fd, guint8 count);
+gboolean tape_setcompression(int fd, gboolean on);
+
+gboolean tape_offl(int fd);
+
+DeviceStatusFlags tape_is_tape_device(int fd);
+DeviceStatusFlags tape_is_ready(int fd, TapeDevice *t_self);
#define tape_device_read_size(self) \
- (((TapeDevice *)(self))->private->read_buffer_size? \
- ((TapeDevice *)(self))->private->read_buffer_size : ((Device *)(self))->block_size)
+ (((TapeDevice *)(self))->private->read_block_size? \
+ ((TapeDevice *)(self))->private->read_block_size : ((Device *)(self))->block_size)
+
+/*
+ * Our device-specific properties.
+ */
+
+#define PROPERTY_BROKEN_GMT_ONLINE (device_property_broken_gmt_online.ID)
+#define PROPERTY_FSF (device_property_fsf.ID)
+#define PROPERTY_FSF_AFTER_FILEMARK (device_property_fsf_after_filemark.ID)
+#define PROPERTY_BSF (device_property_bsf.ID)
+#define PROPERTY_FSR (device_property_fsr.ID)
+#define PROPERTY_BSR (device_property_bsr.ID)
+#define PROPERTY_EOM (device_property_eom.ID)
+#define PROPERTY_BSF_AFTER_EOM (device_property_bsf_after_eom.ID)
+#define PROPERTY_NONBLOCKING_OPEN (device_property_nonblocking_open.ID)
+#define PROPERTY_FINAL_FILEMARKS (device_property_final_filemarks.ID)
+
+static DevicePropertyBase device_property_broken_gmt_online;
+static DevicePropertyBase device_property_fsf;
+static DevicePropertyBase device_property_fsf_after_filemark;
+static DevicePropertyBase device_property_bsf;
+static DevicePropertyBase device_property_fsr;
+static DevicePropertyBase device_property_bsr;
+static DevicePropertyBase device_property_eom;
+static DevicePropertyBase device_property_bsf_after_eom;
+static DevicePropertyBase device_property_nonblocking_open;
+static DevicePropertyBase device_property_final_filemarks;
+static DevicePropertyBase device_property_read_buffer_size; /* old name for READ_BLOCK_SIZE */
/* here are local prototypes */
static void tape_device_init (TapeDevice * o);
GValue *val, PropertySurety surety, PropertySource source);
static gboolean tape_device_set_compression_fn(Device *p_self, DevicePropertyBase *base,
GValue *val, PropertySurety surety, PropertySource source);
-static gboolean tape_device_set_read_buffer_size_fn(Device *p_self, DevicePropertyBase *base,
+static gboolean tape_device_get_read_block_size_fn(Device *p_self, DevicePropertyBase *base,
+ GValue *val, PropertySurety *surety, PropertySource *source);
+static gboolean tape_device_set_read_block_size_fn(Device *p_self, DevicePropertyBase *base,
GValue *val, PropertySurety surety, PropertySource source);
static void tape_device_open_device (Device * self, char * device_name, char * device_type, char * device_node);
static Device * tape_device_factory (char * device_name, char * device_type, char * device_node);
static gboolean tape_device_finish_file (Device * self);
static dumpfile_t * tape_device_seek_file (Device * self, guint file);
static gboolean tape_device_seek_block (Device * self, guint64 block);
+static gboolean tape_device_eject (Device * self);
static gboolean tape_device_finish (Device * self);
static IoResult tape_device_robust_read (TapeDevice * self, void * buf,
- int * count);
-static IoResult tape_device_robust_write (TapeDevice * self, void * buf, int count);
+ int * count, char **errmsg);
+static IoResult tape_device_robust_write (TapeDevice * self, void * buf, int count, char **errmsg);
static gboolean tape_device_fsf (TapeDevice * self, guint count);
-static gboolean tape_device_bsf (TapeDevice * self, guint count, guint file);
static gboolean tape_device_fsr (TapeDevice * self, guint count);
static gboolean tape_device_bsr (TapeDevice * self, guint count, guint file, guint block);
static gboolean tape_device_eod (TapeDevice * self);
GType tape_device_get_type (void)
{
static GType type = 0;
-
+
if G_UNLIKELY(type == 0) {
static const GTypeInfo info = {
sizeof (TapeDeviceClass),
(GInstanceInitFunc) tape_device_init,
NULL
};
-
+
type = g_type_register_static (TYPE_DEVICE, "TapeDevice",
&info, (GTypeFlags)0);
}
return type;
}
-static void
+static void
tape_device_init (TapeDevice * self) {
Device * d_self;
GValue response;
self->fsr = FALSE;
self->bsr = FALSE;
self->eom = FALSE;
+ self->leom = FALSE;
self->bsf_after_eom = FALSE;
g_value_init(&response, G_TYPE_BOOLEAN);
&response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
device_set_simple_property(d_self, PROPERTY_EOM,
&response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
+ device_set_simple_property(d_self, PROPERTY_LEOM,
+ &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
device_set_simple_property(d_self, PROPERTY_BSF_AFTER_EOM,
&response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
&response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
g_value_unset(&response);
- self->private->read_buffer_size = 0;
+ self->private->read_block_size = 0;
g_value_init(&response, G_TYPE_UINT);
- g_value_set_uint(&response, self->private->read_buffer_size);
- device_set_simple_property(d_self, PROPERTY_READ_BUFFER_SIZE,
+ g_value_set_uint(&response, self->private->read_block_size);
+ device_set_simple_property(d_self, PROPERTY_READ_BLOCK_SIZE,
&response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
g_value_unset(&response);
g_value_unset(&response);
g_value_init(&response, G_TYPE_BOOLEAN);
- g_value_set_boolean(&response, FALSE);
+ g_value_set_boolean(&response, TRUE);
device_set_simple_property(d_self, PROPERTY_APPENDABLE,
&response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
g_value_unset(&response);
&response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
g_value_unset(&response);
+ g_value_init(&response, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&response, FALSE);
+ device_set_simple_property(d_self, PROPERTY_FULL_DELETION,
+ &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
+ g_value_unset(&response);
+
g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
device_set_simple_property(d_self, PROPERTY_MEDIUM_ACCESS_TYPE,
amfree(self->private);
}
-static void
+static void
tape_device_class_init (TapeDeviceClass * c)
{
DeviceClass *device_class = (DeviceClass *)c;
GObjectClass *g_object_class = (GObjectClass *)c;
parent_class = g_type_class_ref (TYPE_DEVICE);
-
+
device_class->open_device = tape_device_open_device;
device_class->read_label = tape_device_read_label;
device_class->write_block = tape_device_write_block;
device_class->finish_file = tape_device_finish_file;
device_class->seek_file = tape_device_seek_file;
device_class->seek_block = tape_device_seek_block;
+ device_class->eject = tape_device_eject;
device_class->finish = tape_device_finish;
-
+
g_object_class->finalize = tape_device_finalize;
}
NULL,
tape_device_set_compression_fn);
- device_class_register_property(device_class, PROPERTY_READ_BUFFER_SIZE,
+ device_class_register_property(device_class, PROPERTY_READ_BLOCK_SIZE,
+ PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+ tape_device_get_read_block_size_fn,
+ tape_device_set_read_block_size_fn);
+
+ device_class_register_property(device_class, device_property_read_buffer_size.ID,
+ PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+ tape_device_get_read_block_size_fn,
+ tape_device_set_read_block_size_fn);
+
+ /* add the ability to set LEOM to FALSE, for testing purposes */
+ device_class_register_property(device_class, PROPERTY_LEOM,
PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
device_simple_property_get_fn,
- tape_device_set_read_buffer_size_fn);
+ tape_device_set_feature_property_fn);
}
static gboolean
self->bsf_after_eom = new_bool;
else if (base->ID == PROPERTY_NONBLOCKING_OPEN)
self->nonblocking_open = new_bool;
+ else if (base->ID == PROPERTY_LEOM)
+ self->leom = new_bool;
else
return FALSE; /* shouldn't happen */
}
static gboolean
-tape_device_set_read_buffer_size_fn(Device *p_self, DevicePropertyBase *base,
+tape_device_get_read_block_size_fn(Device *p_self, DevicePropertyBase *base G_GNUC_UNUSED,
+ GValue *val, PropertySurety *surety, PropertySource *source)
+{
+ /* use the READ_BLOCK_SIZE, even if we're invoked to get the old READ_BUFFER_SIZE */
+ return device_simple_property_get_fn(p_self, &device_property_read_block_size,
+ val, surety, source);
+}
+
+static gboolean
+tape_device_set_read_block_size_fn(Device *p_self, DevicePropertyBase *base G_GNUC_UNUSED,
GValue *val, PropertySurety surety, PropertySource source)
{
TapeDevice *self = TAPE_DEVICE(p_self);
- guint buffer_size = g_value_get_uint(val);
+ guint read_block_size = g_value_get_uint(val);
- if (buffer_size != 0 &&
- ((gsize)buffer_size < p_self->block_size ||
- (gsize)buffer_size > p_self->max_block_size))
+ if (read_block_size != 0 &&
+ ((gsize)read_block_size < p_self->block_size ||
+ (gsize)read_block_size > p_self->max_block_size))
return FALSE;
- self->private->read_buffer_size = buffer_size;
+ self->private->read_block_size = read_block_size;
- return device_simple_property_set_fn(p_self, base, val, surety, source);
+ /* use the READ_BLOCK_SIZE, even if we're invoked to get the old READ_BUFFER_SIZE */
+ return device_simple_property_set_fn(p_self, &device_property_read_block_size,
+ val, surety, source);
}
void tape_device_register(void) {
G_TYPE_BOOLEAN, "bsr",
"Does this drive support the MTBSR command?");
- /* FIXME: Is this feature even useful? */
device_property_fill_and_register(&device_property_eom,
G_TYPE_BOOLEAN, "eom",
"Does this drive support the MTEOM command?");
G_TYPE_UINT, "final_filemarks",
"How many filemarks to write after the last tape file?" );
+ device_property_fill_and_register(&device_property_read_buffer_size,
+ G_TYPE_UINT, "read_buffer_size",
+ "(deprecated name for READ_BLOCK_SIZE)");
+
/* Then the device itself */
register_device(tape_device_factory, device_prefix_list);
}
}
static void
-tape_device_open_device (Device * d_self, char * device_name G_GNUC_UNUSED,
- char * device_type G_GNUC_UNUSED, char * device_node) {
+tape_device_open_device (Device * dself, char * device_name,
+ char * device_type, char * device_node) {
TapeDevice * self;
+ GValue val;
- self = TAPE_DEVICE(d_self);
+ self = TAPE_DEVICE(dself);
self->fd = -1;
self->private->device_filename = stralloc(device_node);
- /* Get tape drive/OS info */
- tape_device_detect_capabilities(self);
-
- /* Chain up */
- if (parent_class->open_device) {
- parent_class->open_device(d_self, device_node, device_type, device_node);
- }
-}
-
-void
-tape_device_set_capabilities(TapeDevice *self,
- gboolean fsf, PropertySurety fsf_surety, PropertySource fsf_source,
- gboolean fsf_after_filemark, PropertySurety faf_surety, PropertySource faf_source,
- gboolean bsf, PropertySurety bsf_surety, PropertySource bsf_source,
- gboolean fsr, PropertySurety fsr_surety, PropertySource fsr_source,
- gboolean bsr, PropertySurety bsr_surety, PropertySource bsr_source,
- gboolean eom, PropertySurety eom_surety, PropertySource eom_source,
- gboolean bsf_after_eom, PropertySurety bae_surety, PropertySource bae_source,
- guint final_filemarks, PropertySurety ff_surety, PropertySource ff_source)
-{
- Device *dself = DEVICE(self);
- GValue val;
-
- /* this function is called by tape_device_detect_capabilities, and basically
- * exists to take care of the GValue mechanics in one place */
-
- g_assert(final_filemarks == 1 || final_filemarks == 2);
-
+ /* Set tape drive/OS info */
bzero(&val, sizeof(val));
g_value_init(&val, G_TYPE_BOOLEAN);
- self->fsf = fsf;
- g_value_set_boolean(&val, fsf);
- device_set_simple_property(dself, PROPERTY_FSF, &val, fsf_surety, fsf_source);
+ self->fsf = TRUE;
+ g_value_set_boolean(&val, self->fsf);
+ device_set_simple_property(dself, PROPERTY_FSF, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
+
+ self->fsf_after_filemark = DEFAULT_FSF_AFTER_FILEMARK;
+ g_value_set_boolean(&val, self->fsf_after_filemark);
+ device_set_simple_property(dself, PROPERTY_FSF_AFTER_FILEMARK, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
- self->fsf_after_filemark = fsf_after_filemark;
- g_value_set_boolean(&val, fsf_after_filemark);
- device_set_simple_property(dself, PROPERTY_FSF_AFTER_FILEMARK, &val, faf_surety, faf_source);
+ self->bsf = TRUE;
+ g_value_set_boolean(&val, self->bsf);
+ device_set_simple_property(dself, PROPERTY_BSF, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
- self->bsf = bsf;
- g_value_set_boolean(&val, bsf);
- device_set_simple_property(dself, PROPERTY_BSF, &val, bsf_surety, bsf_source);
+ self->fsr = TRUE;
+ g_value_set_boolean(&val, self->fsr);
+ device_set_simple_property(dself, PROPERTY_FSR, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
- self->fsr = fsr;
- g_value_set_boolean(&val, fsr);
- device_set_simple_property(dself, PROPERTY_FSR, &val, fsr_surety, fsr_source);
+ self->bsr = TRUE;
+ g_value_set_boolean(&val, self->bsr);
+ device_set_simple_property(dself, PROPERTY_BSR, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
- self->bsr = bsr;
- g_value_set_boolean(&val, bsr);
- device_set_simple_property(dself, PROPERTY_BSR, &val, bsr_surety, bsr_source);
+ self->eom = TRUE;
+ g_value_set_boolean(&val, self->eom);
+ device_set_simple_property(dself, PROPERTY_EOM, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
- self->eom = eom;
- g_value_set_boolean(&val, eom);
- device_set_simple_property(dself, PROPERTY_EOM, &val, eom_surety, eom_source);
+ self->leom = FALSE;
+ g_value_set_boolean(&val, self->leom);
+ device_set_simple_property(dself, PROPERTY_LEOM, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
- self->bsf_after_eom = bsf_after_eom;
- g_value_set_boolean(&val, bsf_after_eom);
- device_set_simple_property(dself, PROPERTY_BSF_AFTER_EOM, &val, bae_surety, bae_source);
+ self->bsf_after_eom = FALSE;
+ g_value_set_boolean(&val, self->bsf_after_eom);
+ device_set_simple_property(dself, PROPERTY_BSF_AFTER_EOM, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
g_value_unset(&val);
g_value_init(&val, G_TYPE_UINT);
- self->final_filemarks = final_filemarks;
- g_value_set_uint(&val, final_filemarks);
- device_set_simple_property(dself, PROPERTY_FINAL_FILEMARKS, &val, ff_surety, ff_source);
+ self->final_filemarks = 2;
+ g_value_set_uint(&val, self->final_filemarks);
+ device_set_simple_property(dself, PROPERTY_FINAL_FILEMARKS, &val, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
g_value_unset(&val);
+
+ /* Chain up */
+ if (parent_class->open_device) {
+ parent_class->open_device(dself, device_name, device_type, device_node);
+ }
}
+
static DeviceStatusFlags tape_device_read_label(Device * dself) {
TapeDevice * self;
char * header_buffer;
IoResult result;
dumpfile_t *header;
DeviceStatusFlags new_status;
+ char *msg = NULL;
self = TAPE_DEVICE(dself);
amfree(dself->volume_label);
amfree(dself->volume_time);
- amfree(dself->volume_header);
+ dumpfile_free(dself->volume_header);
+ dself->volume_header = NULL;
if (device_in_error(self)) return dself->status;
- header = dself->volume_header = g_new(dumpfile_t, 1);
- fh_init(header);
-
if (self->fd == -1) {
self->fd = try_open_tape_device(self, self->private->device_filename);
/* if the open failed, then try_open_tape_device already set the
/* Rewind it. */
if (!tape_rewind(self->fd)) {
device_set_error(dself,
- vstrallocf(_("Error rewinding device %s"), self->private->device_filename),
+ vstrallocf(_("Error rewinding device %s to read label: %s"),
+ self->private->device_filename, strerror(errno)),
DEVICE_STATUS_DEVICE_ERROR
| DEVICE_STATUS_VOLUME_ERROR);
- robust_close(self->fd);
return dself->status;
}
buffer_len = tape_device_read_size(self);
header_buffer = malloc(buffer_len);
- result = tape_device_robust_read(self, header_buffer, &buffer_len);
+ result = tape_device_robust_read(self, header_buffer, &buffer_len, &msg);
if (result != RESULT_SUCCESS) {
free(header_buffer);
tape_rewind(self->fd);
/* I/O error. */
- if (result == RESULT_NO_DATA) {
+ switch (result) {
+ case RESULT_NO_DATA:
+ msg = stralloc(_("no data"));
new_status = (DEVICE_STATUS_VOLUME_ERROR |
DEVICE_STATUS_VOLUME_UNLABELED);
- } else {
+ header = dself->volume_header = g_new(dumpfile_t, 1);
+ fh_init(header);
+ break;
+
+ case RESULT_SMALL_BUFFER:
+ msg = stralloc(_("block size too small"));
+ new_status = (DEVICE_STATUS_DEVICE_ERROR |
+ DEVICE_STATUS_VOLUME_ERROR);
+ header = dself->volume_header = g_new(dumpfile_t, 1);
+ fh_init(header);
+ header->type = F_WEIRD;
+ break;
+
+ default:
+ msg = stralloc(_("unknown error"));
+ case RESULT_ERROR:
new_status = (DEVICE_STATUS_DEVICE_ERROR |
DEVICE_STATUS_VOLUME_ERROR |
DEVICE_STATUS_VOLUME_UNLABELED);
+ break;
}
- device_set_error(dself, stralloc(_("Error reading Amanda header")), new_status);
+ device_set_error(dself,
+ g_strdup_printf(_("Error reading Amanda header: %s"),
+ msg? msg : _("unknown error")),
+ new_status);
+ amfree(msg);
return dself->status;
}
+ header = dself->volume_header = g_new(dumpfile_t, 1);
+ fh_init(header);
+
parse_file_header(header_buffer, header, buffer_len);
amfree(header_buffer);
if (header->type != F_TAPESTART) {
TapeDevice * self;
char *replacement_buffer = NULL;
IoResult result;
+ char *msg = NULL;
self = TAPE_DEVICE(pself);
size = pself->block_size;
}
- result = tape_device_robust_write(self, data, size);
+ result = tape_device_robust_write(self, data, size, &msg);
amfree(replacement_buffer);
switch (result) {
device_set_error(pself,
stralloc(_("No space left on device")),
DEVICE_STATUS_VOLUME_ERROR);
- pself->is_eof = TRUE;
+ pself->is_eom = TRUE;
return FALSE;
default:
+ msg = stralloc(_("unknown error"));
case RESULT_ERROR:
device_set_error(pself,
- vstrallocf(_("Error writing block: %s"), strerror(errno)),
+ g_strdup_printf(_("Error writing block: %s"), msg),
DEVICE_STATUS_DEVICE_ERROR);
+ amfree(msg);
return FALSE;
}
int size;
IoResult result;
gssize read_block_size = tape_device_read_size(pself);
-
+ char *msg = NULL;
+
self = TAPE_DEVICE(pself);
g_assert(self->fd >= 0);
}
size = *size_req;
- result = tape_device_robust_read(self, buf, &size);
+ result = tape_device_robust_read(self, buf, &size, &msg);
switch (result) {
case RESULT_SUCCESS:
*size_req = size;
}
g_assert (new_size > (gsize)*size_req);
- g_warning("Device %s indicated blocksize %zd was too small; using %zd.",
+ g_info("Device %s indicated blocksize %zd was too small; using %zd.",
pself->device_name, (gsize)*size_req, new_size);
*size_req = (int)new_size;
- self->private->read_buffer_size = new_size;
+ self->private->read_block_size = new_size;
bzero(&newval, sizeof(newval));
g_value_init(&newval, G_TYPE_UINT);
- g_value_set_uint(&newval, self->private->read_buffer_size);
- device_set_simple_property(pself, PROPERTY_READ_BUFFER_SIZE,
+ g_value_set_uint(&newval, self->private->read_block_size);
+ device_set_simple_property(pself, PROPERTY_READ_BLOCK_SIZE,
&newval, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
g_value_unset(&newval);
return -1;
default:
+ msg = stralloc(_("unknown error"));
+ case RESULT_ERROR:
device_set_error(pself,
- vstrallocf(_("Error reading from tape device: %s"), strerror(errno)),
+ vstrallocf(_("Error reading from tape device: %s"), msg),
DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
+ amfree(msg);
return -1;
}
IoResult result;
dumpfile_t * header;
char * header_buf;
- int header_size;
- gboolean header_fits;
Device * d_self = (Device*)self;
+ char *msg = NULL;
+
tape_rewind(self->fd);
-
+
header = make_tapestart_header(d_self, label, timestamp);
g_assert(header != NULL);
- header_buf = device_build_amanda_header(d_self, header, &header_size,
- &header_fits);
- amfree(header);
- g_assert(header_buf != NULL);
-
- if (!header_fits) {
- amfree(header_buf);
+ header_buf = device_build_amanda_header(d_self, header, NULL);
+ if (header_buf == NULL) {
device_set_error(d_self,
stralloc(_("Tapestart header won't fit in a single block!")),
DEVICE_STATUS_DEVICE_ERROR);
+ dumpfile_free(header);
return FALSE;
}
+ dumpfile_free(d_self->volume_header);
+ d_self->volume_header = NULL;
- g_assert(header_size >= (int)d_self->min_block_size);
- result = tape_device_robust_write(self, header_buf, header_size);
+ result = tape_device_robust_write(self, header_buf, d_self->block_size, &msg);
if (result != RESULT_SUCCESS) {
- device_set_error(d_self,
- vstrallocf(_("Error writing tapestart header: %s"), strerror(errno)),
+ device_set_error(d_self,
+ g_strdup_printf(_("Error writing tapestart header: %s"),
+ (result == RESULT_ERROR)? msg : _("out of space")),
DEVICE_STATUS_DEVICE_ERROR);
+
+ if (result == RESULT_NO_SPACE)
+ d_self->is_eom = TRUE;
+
+ amfree(msg);
+ dumpfile_free(header);
amfree(header_buf);
return FALSE;
}
vstrallocf(_("Error writing filemark: %s"),
strerror(errno)),
DEVICE_STATUS_DEVICE_ERROR|DEVICE_STATUS_VOLUME_ERROR);
+ /* can't tell if this was EOM or not, so assume it is */
+ d_self->is_eom = TRUE;
+ dumpfile_free(header);
return FALSE;
}
+ d_self->volume_header = header;
return TRUE;
-
}
-static gboolean
+static gboolean
tape_device_start (Device * d_self, DeviceAccessMode mode, char * label,
char * timestamp) {
TapeDevice * self;
return FALSE;
} else if (!tape_rewind(self->fd)) {
device_set_error(d_self,
- vstrallocf(_("Couldn't rewind device: %s"), strerror(errno)),
+ vstrallocf(_("Error rewinding device to start: %s"), strerror(errno)),
DEVICE_STATUS_DEVICE_ERROR);
return FALSE;
}
return FALSE;
}
break;
-
+
case ACCESS_READ:
if (d_self->volume_label == NULL && device_read_label(d_self) != DEVICE_STATUS_SUCCESS) {
/* device_read_label already set our error message */
if (!tape_rewind(self->fd)) {
device_set_error(d_self,
- vstrallocf(_("Couldn't rewind device: %s"), strerror(errno)),
+ vstrallocf(_("Error rewinding device after reading label: %s"), strerror(errno)),
DEVICE_STATUS_DEVICE_ERROR);
return FALSE;
}
TapeDevice * self;
IoResult result;
char * amanda_header;
- int header_size;
- gboolean header_fits;
+ char *msg = NULL;
self = TAPE_DEVICE(d_self);
/* Make the Amanda header suitable for writing to the device. */
/* Then write the damn thing. */
- amanda_header = device_build_amanda_header(d_self, info,
- &header_size, &header_fits);
- if (!header_fits) {
+ amanda_header = device_build_amanda_header(d_self, info, NULL);
+ if (amanda_header == NULL) {
device_set_error(d_self,
stralloc(_("Amanda file header won't fit in a single block!")),
DEVICE_STATUS_DEVICE_ERROR);
return FALSE;
}
- result = tape_device_robust_write(self, amanda_header, header_size);
+
+ result = tape_device_robust_write(self, amanda_header, d_self->block_size, &msg);
if (result != RESULT_SUCCESS) {
device_set_error(d_self,
- vstrallocf(_("Error writing file header: %s"), strerror(errno)),
+ vstrallocf(_("Error writing file header: %s"),
+ (result == RESULT_ERROR)? msg : _("out of space")),
DEVICE_STATUS_DEVICE_ERROR);
+
+ if (result == RESULT_NO_SPACE)
+ d_self->is_eom = TRUE;
+
amfree(amanda_header);
+ amfree(msg);
return FALSE;
}
amfree(amanda_header);
device_set_error(d_self,
vstrallocf(_("Error writing filemark: %s"), strerror(errno)),
DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
+ /* can't tell if this was EOM or not, so assume it is */
+ d_self->is_eom = TRUE;
return FALSE;
}
return TRUE;
}
-static dumpfile_t *
+static dumpfile_t *
tape_device_seek_file (Device * d_self, guint file) {
TapeDevice * self;
+ gint got_file;
int difference;
char * header_buffer;
dumpfile_t * rval;
int buffer_len;
IoResult result;
+ char *msg;
self = TAPE_DEVICE(d_self);
d_self->is_eof = FALSE;
d_self->block = 0;
+reseek:
if (difference > 0) {
/* Seeking forwards */
if (!tape_device_fsf(self, difference)) {
DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
return NULL;
}
- } else if (difference < 0) {
- /* Seeking backwards */
- if (!tape_device_bsf(self, -difference, d_self->file)) {
- tape_rewind(self->fd);
- device_set_error(d_self,
- vstrallocf(_("Could not seek backward to file %d"), file),
- DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
- return NULL;
- }
+ } else { /* (difference <= 0) */
+ /* Seeking backwards, or to this file itself */
+
+ /* if the drive supports bsf, we can do this the fancy way */
+ if (self->bsf) {
+ /* bsf one more than the difference */
+ if (!tape_bsf(self->fd, -difference + 1)) {
+ tape_rewind(self->fd);
+ device_set_error(d_self,
+ vstrallocf(_("Could not seek backward to file %d"), file),
+ DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
+ return NULL;
+ }
+
+ /* now we are on the BOT side of the desired filemark, so FSF to get to the
+ * EOT side of it */
+ if (!tape_device_fsf(self, 1)) {
+ tape_rewind(self->fd);
+ device_set_error(d_self,
+ vstrallocf(_("Could not seek forward to file %d"), file),
+ DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
+ return NULL;
+ }
+ } else {
+ /* no BSF, so just rewind and seek forward */
+ if (!tape_rewind(self->fd)) {
+ device_set_error(d_self,
+ vstrallocf(_("Could not rewind device while emulating BSF")),
+ DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
+ }
+
+ if (!tape_device_fsf(self, file)) {
+ tape_rewind(self->fd);
+ device_set_error(d_self,
+ vstrallocf(_("Could not seek forward to file %d"), file),
+ DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
+ return NULL;
+ }
+ }
+ }
+
+ /* double-check that we're on the right fileno, if possible. This is most
+ * likely a programming error if it occurs, but could also be due to a weird
+ * tape drive or driver (and that would *never* happen, right?) */
+ got_file = tape_fileno(self->fd);
+ if (got_file >= 0 && (guint)got_file != file) {
+ device_set_error(d_self,
+ vstrallocf(_("Could not seek to file %d correctly; got %d"),
+ file, got_file),
+ DEVICE_STATUS_DEVICE_ERROR);
+ d_self->file = (guint)got_file;
+ return NULL;
}
buffer_len = tape_device_read_size(d_self);
header_buffer = malloc(buffer_len);
d_self->is_eof = FALSE;
- result = tape_device_robust_read(self, header_buffer, &buffer_len);
+ result = tape_device_robust_read(self, header_buffer, &buffer_len, &msg);
if (result != RESULT_SUCCESS) {
free(header_buffer);
tape_rewind(self->fd);
- if (result == RESULT_NO_DATA) {
+ switch (result) {
+ case RESULT_NO_DATA:
/* If we read 0 bytes, that means we encountered a double
* filemark, which indicates end of tape. This should
* work even with QIC tapes on operating systems with
* proper support. */
d_self->file = file; /* other attributes are already correct */
return make_tapeend_header();
+
+ case RESULT_SMALL_BUFFER:
+ msg = stralloc(_("block size too small"));
+ break;
+
+ default:
+ msg = stralloc(_("unknown error"));
+ case RESULT_ERROR:
+ break;
}
- /* I/O error. */
device_set_error(d_self,
- stralloc(_("Error reading Amanda header")),
+ g_strdup_printf(_("Error reading Amanda header: %s"), msg),
DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
+ amfree(msg);
return NULL;
}
-
+
rval = g_new(dumpfile_t, 1);
parse_file_header(header_buffer, rval, buffer_len);
amfree(header_buffer);
case F_SPLIT_DUMPFILE:
break;
+ case F_NOOP:
+ /* a NOOP is written on QIC tapes to avoid writing two sequential
+ * filemarks when closing a device in WRITE or APPEND mode. In this
+ * case, we just seek to the next file. */
+ amfree(rval);
+ file++;
+ difference = 1;
+ goto reseek;
+
default:
tape_rewind(self->fd);
device_set_error(d_self,
return rval;
}
-static gboolean
+static gboolean
tape_device_seek_block (Device * d_self, guint64 block) {
TapeDevice * self;
int difference;
if (device_in_error(self)) return FALSE;
difference = block - d_self->block;
-
+
if (difference > 0) {
if (!tape_device_fsr(self, difference)) {
device_set_error(d_self,
- vstrallocf(_("Could not seek forward to block %ju"), (uintmax_t)block),
+ vstrallocf(_("Could not seek forward to block %ju: %s"), (uintmax_t)block, strerror(errno)),
DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
return FALSE;
}
} else if (difference < 0) {
if (!tape_device_bsr(self, difference, d_self->file, d_self->block)) {
device_set_error(d_self,
- vstrallocf(_("Could not seek backward to block %ju"), (uintmax_t)block),
+ vstrallocf(_("Could not seek backward to block %ju: %s"), (uintmax_t)block, strerror(errno)),
DEVICE_STATUS_VOLUME_ERROR | DEVICE_STATUS_DEVICE_ERROR);
return FALSE;
}
return TRUE;
}
-static gboolean
-tape_device_finish (Device * d_self) {
+static gboolean
+tape_device_eject (Device * d_self) {
TapeDevice * self;
self = TAPE_DEVICE(d_self);
if (device_in_error(self)) return FALSE;
- if (d_self->access_mode == ACCESS_NULL)
+ /* Open the device if not already opened */
+ if (self->fd == -1) {
+ self->fd = try_open_tape_device(self, self->private->device_filename);
+ /* if the open failed, then try_open_tape_device already set the
+ * approppriate error status */
+ if (self->fd == -1)
+ return FALSE;
+ }
+
+ /* Rewind it. */
+ if (!tape_rewind(self->fd)) {
+ device_set_error(d_self,
+ vstrallocf(_("Error rewinding device %s before ejecting: %s"),
+ self->private->device_filename, strerror(errno)),
+ DEVICE_STATUS_DEVICE_ERROR
+ | DEVICE_STATUS_VOLUME_ERROR);
+ return FALSE;
+ }
+
+ if (tape_offl(self->fd))
+ return TRUE;
+
+ device_set_error(d_self,
+ vstrallocf(_("Error ejecting device %s: %s\n"),
+ self->private->device_filename, strerror(errno)),
+ DEVICE_STATUS_DEVICE_ERROR);
+
+ return FALSE;
+}
+
+static gboolean
+tape_device_finish (Device * d_self) {
+ TapeDevice * self;
+ char *msg = NULL;
+
+ self = TAPE_DEVICE(d_self);
+
+ if (device_in_error(self))
+ goto finish_error;
+
+ /* if we're already in ACCESS_NULL, then there are no filemarks or anything
+ * to worry about, but we need to release the kernel device */
+ if (d_self->access_mode == ACCESS_NULL) {
+ robust_close(self->fd);
+ self->fd = -1;
return TRUE;
+ }
/* Polish off this file, if relevant. */
if (d_self->in_file && IS_WRITABLE_ACCESS_MODE(d_self->access_mode)) {
if (!device_finish_file(d_self))
- return FALSE;
+ goto finish_error;
}
- /* Write an extra filemark, if needed. The OS will give us one for
- sure. */
- /* device_finish_file already wrote one for us */
- /*
- if (self->final_filemarks > 1 &&
+ /* Straighten out the filemarks. We already wrote one in finish_file, and
+ * the device driver will write another filemark when we rewind. This means
+ * that, if we do nothing, we'll get two filemarks. If final_filemarks is
+ * 1, this would be wrong, so in this case we insert a F_NOOP header between
+ * the two filemarks. */
+ if (self->final_filemarks == 1 &&
IS_WRITABLE_ACCESS_MODE(d_self->access_mode)) {
- if (!tape_weof(self->fd, 1)) {
- device_set_error(d_self,
- vstrallocf(_("Error writing final filemark: %s"), strerror(errno)),
- DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
- return FALSE;
- }
+ dumpfile_t file;
+ char *header;
+ int result;
+
+ /* write a F_NOOP header */
+ fh_init(&file);
+ file.type = F_NOOP;
+ header = device_build_amanda_header(d_self, &file, NULL);
+ if (!header) {
+ device_set_error(d_self,
+ stralloc(_("Amanda file header won't fit in a single block!")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ goto finish_error;
+ }
+
+ result = tape_device_robust_write(self, header, d_self->block_size, &msg);
+ if (result != RESULT_SUCCESS) {
+ device_set_error(d_self,
+ vstrallocf(_("Error writing file header: %s"),
+ (result == RESULT_ERROR)? msg : _("out of space")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ amfree(header);
+ amfree(msg);
+ goto finish_error;
+ }
+ amfree(header);
}
- */
- /* Rewind. */
+ /* Rewind (the kernel will write a filemark first) */
if (!tape_rewind(self->fd)) {
device_set_error(d_self,
- vstrallocf(_("Couldn't rewind device: %s"), strerror(errno)),
+ vstrallocf(_("Couldn't rewind device to finish: %s"), strerror(errno)),
DEVICE_STATUS_DEVICE_ERROR);
- return FALSE;
+ goto finish_error;
}
d_self->is_eof = FALSE;
d_self->access_mode = ACCESS_NULL;
+ /* release the kernel's device */
+ robust_close(self->fd);
+ self->fd = -1;
+
return TRUE;
+
+finish_error:
+ d_self->access_mode = ACCESS_NULL;
+
+ /* release the kernel's device */
+ robust_close(self->fd);
+ self->fd = -1;
+
+ return FALSE;
}
/* Works just like read(), except for the following:
* 1) Retries on EINTR & friends.
* 2) Stores count in parameter, not return value.
- * 3) Provides explicit return result. */
+ * 3) Provides explicit return result.
+ * *errmsg is only set on RESULT_ERROR.
+ */
static IoResult
-tape_device_robust_read (TapeDevice * self, void * buf, int * count) {
+tape_device_robust_read (TapeDevice * self, void * buf, int * count, char **errmsg) {
Device * d_self;
int result;
#endif
)) {
/* Buffer too small. */
+ g_warning("Buffer is too small (%d bytes) from %s: %s",
+ *count, self->private->device_filename, strerror(errno));
return RESULT_SMALL_BUFFER;
} else {
- device_set_error(d_self,
- vstrallocf(_("Error reading %d bytes from %s: %s"),
- *count, self->private->device_filename, strerror(errno)),
- DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
+ *errmsg = g_strdup_printf(_("Error reading %d bytes from %s: %s"),
+ *count, self->private->device_filename, strerror(errno));
return RESULT_ERROR;
}
}
-
}
g_assert_not_reached();
#endif
}
-static IoResult
-tape_device_robust_write (TapeDevice * self, void * buf, int count) {
+/* *errmsg is only set on RESULT_ERROR */
+static IoResult
+tape_device_robust_write (TapeDevice * self, void * buf, int count, char **errmsg) {
Device * d_self;
int result;
+ gboolean retry = FALSE;
d_self = (Device*)self;
-
+
check_resetofs(self, count);
for (;;) {
result = write(self->fd, buf, count);
- if (result == count) {
- /* Success. */
-
- self->private->write_count ++;
+ /* Success. */
+ if (result == count)
return RESULT_SUCCESS;
- } else if (result >= 0) {
- /* write() returned a short count. This should not happen. */
- device_set_error(d_self,
- vstrallocf(_("Mysterious short write on tape device: Tried %d, got %d"),
- count, result),
- DEVICE_STATUS_DEVICE_ERROR);
+
+ if (result > 0) {
+ /* write() returned a short count. This should not happen if the block sizes
+ * are properly aligned. */
+ *errmsg = g_strdup_printf("Short write on tape device: Tried %d, got %d. Is "
+ "the drive using a block size smaller than %d bytes?",
+ count, result, count);
return RESULT_ERROR;
- } else if (0
+ }
+
+ /* Detect LEOM (early warning) and handle properly
+ *
+ * FreeBSD: 0-length write; next write will succeed
+ * http://lists.freebsd.org/pipermail/freebsd-scsi/2010-June/004414.html
+ *
+ * Solaris: 0-length write; next write will succeed
+ * (from Matthew Jacob on FreeBSD thread)
+ *
+ * Linux: -1/ENOSPC; next write will succeed
+ * http://www.mjmwired.net/kernel/Documentation/scsi/st.txt
+ *
+ * HP/UX: -1/ENOSPC; next write will succeed
+ * http://www.adssasia.com/Manual/IBM%203581%20tape%20autoloader.pdf
+ */
+ if (result == 0
+#ifdef ENOSPC
+ || (result < 0 && errno == ENOSPC)
+#endif
+ ) {
+ /* if we've retried once already, then we're probably really out of space */
+ if (retry)
+ return RESULT_NO_SPACE;
+ retry = TRUE;
+ d_self->is_eom = TRUE;
+
+ g_debug("empty write to tape; treating as LEOM early warning and retrying");
+ continue;
+ }
+
+ /* at this point result < 0, so an error occurred - sort out what */
+
+ if (0
#ifdef EAGAIN
|| errno == EAGAIN
#endif
return RESULT_NO_SPACE;
} else {
/* WTF */
- device_set_error(d_self,
- vstrallocf(_("Kernel gave unexpected write() result of \"%s\" on device %s"),
- strerror(errno), self->private->device_filename),
- DEVICE_STATUS_DEVICE_ERROR);
+ *errmsg = vstrallocf(_("Kernel gave unexpected write() result of \"%s\" on device %s"),
+ strerror(errno), self->private->device_filename);
return RESULT_ERROR;
}
}
buffer_size = tape_device_read_size(self);
- buffer = malloc(sizeof(buffer_size));
+ buffer = malloc(buffer_size);
for (i = 0; i < count || count < 0;) {
int result;
}
}
}
-
+
amfree(buffer);
return count;
}
-/* FIXME: Make sure that there are no cycles in reimplementation
- dependencies. */
-
-static gboolean
+static gboolean
tape_device_fsf (TapeDevice * self, guint count) {
if (self->fsf) {
return tape_fsf(self->fd, count);
}
}
-/* Seek back over count + 1 filemarks to the start of the given file. */
-static gboolean
-tape_device_bsf (TapeDevice * self, guint count, guint file) {
- if (self->bsf) {
- /* The BSF operation is not very smart; it includes the
- filemark of the present file as part of the count, and seeks
- to the wrong (BOT) side of the filemark. We compensate for
- this by seeking one filemark too many, then FSFing back over
- it.
-
- If this procedure fails for some reason, we can still try
- the backup plan. */
- if (tape_bsf(self->fd, count + 1) &&
- tape_device_fsf(self, 1))
- return TRUE;
- } /* Fall through to backup plan. */
-
- /* We rewind the tape, then seek forward the given number of
- files. */
- if (!tape_rewind(self->fd))
- return FALSE;
-
- return tape_device_fsf(self, file);
-}
-
-static gboolean
+static gboolean
tape_device_fsr (TapeDevice * self, guint count) {
if (self->fsr) {
return tape_fsr(self->fd, count);
/* Seek back the given number of blocks to block number block within
* the current file, numbered file. */
-static gboolean
+static gboolean
tape_device_bsr (TapeDevice * self, guint count, guint file, guint block) {
if (self->bsr) {
return tape_bsr(self->fd, count);
- } else {
- /* We BSF, then FSR. */
- if (!tape_device_bsf(self, 0, file))
+ } else if (self->bsf && self->fsf) {
+ /* BSF, FSF to the right side of the filemark, and then FSR. */
+ if (!tape_bsf(self->fd, 1))
return FALSE;
-
+
+ if (!tape_fsf(self->fd, 1))
+ return FALSE;
+
+ return tape_device_fsr(self, block);
+ } else {
+ /* rewind, FSF, and FSR */
+ if (!tape_rewind(self->fd))
+ return FALSE;
+
+ if (!tape_device_fsf(self, file))
+ return FALSE;
+
return tape_device_fsr(self, block);
}
g_assert_not_reached();
/* Go to the right place to write more data, and update the file
number if possible. */
-static gboolean
+static gboolean
tape_device_eod (TapeDevice * self) {
Device * d_self;
+ int count;
+
d_self = (Device*)self;
if (self->eom) {
int result;
- result = tape_eod(self->fd);
+ result = tape_eod(self->fd);
if (result == TAPE_OP_ERROR) {
return FALSE;
- } else if (result == TAPE_POSITION_UNKNOWN) {
- d_self->file = -1;
+ } else if (result != TAPE_POSITION_UNKNOWN) {
+ /* great - we just fast-forwarded to EOD, but don't know where we are, so
+ * now we have to rewind and drain all of that data. Warn the user so that
+ * we can skip the fast-forward-rewind stage on the next run */
+ g_warning("Seek to end of tape does not give an accurate tape position; set "
+ "the EOM property to 0 to avoid useless tape movement.");
+ /* and set the property so that next time *this* object is opened for
+ * append, we skip this stage */
+ self->eom = FALSE;
+ /* fall through to draining blocks, below */
} else {
/* We drop by 1 because Device will increment the first
time the user does start_file. */
d_self->file = result - 1;
+ return TRUE;
}
- return TRUE;
- } else {
- int count = 0;
- if (!tape_rewind(self->fd))
- return FALSE;
-
- for (;;) {
- /* We alternately read a block and FSF. If the read is
- successful, then we are not there yet and should FSF
- again. */
- int result;
- result = drain_tape_blocks(self, 1);
- if (result == 1) {
- /* More data, FSF. */
- tape_device_fsf(self, 1);
- count ++;
- } else if (result == 0) {
- /* Finished. */
- d_self->file = count;
- return TRUE;
- } else {
- return FALSE;
- }
- }
+ }
+
+ if (!tape_rewind(self->fd))
+ return FALSE;
+
+ count = 0;
+ for (;;) {
+ /* We alternately read a block and FSF. If the read is
+ successful, then we are not there yet and should FSF
+ again. */
+ int result;
+ result = drain_tape_blocks(self, 1);
+ if (result == 1) {
+ /* More data, FSF. */
+ tape_device_fsf(self, 1);
+ count ++;
+ } else if (result == 0) {
+ /* Finished. */
+ d_self->file = count - 1;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
}
}
device_open_device(rval, device_name, device_type, device_node);
return rval;
}
+
+/*
+ * Tape Operations using the POSIX interface
+ */
+
+/* Having one name for every operation would be too easy. */
+#if !defined(MTCOMPRESSION) && defined(MTCOMP)
+# define MTCOMPRESSION MTCOMP
+#endif
+
+#if !defined(MTSETBLK) && defined(MTSETBSIZ)
+# define MTSETBLK MTSETBSIZ
+#endif
+
+#if !defined(MTEOM) && defined(MTEOD)
+# define MTEOM MTEOD
+#endif
+
+gboolean tape_rewind(int fd) {
+ int count = 5;
+ time_t stop_time;
+
+ /* We will retry this for up to 30 seconds or 5 retries,
+ whichever is less, because some hardware/software combinations
+ (notably EXB-8200 on FreeBSD) can fail to rewind. */
+ stop_time = time(NULL) + 30;
+
+ while (--count >= 0 && time(NULL) < stop_time) {
+ struct mtop mt;
+ mt.mt_op = MTREW;
+ mt.mt_count = 1;
+
+ if (0 == ioctl(fd, MTIOCTOP, &mt))
+ return TRUE;
+
+ sleep(3);
+ }
+
+ return FALSE;
+}
+
+gboolean tape_fsf(int fd, guint count) {
+ struct mtop mt;
+ mt.mt_op = MTFSF;
+ mt.mt_count = count;
+ return 0 == ioctl(fd, MTIOCTOP, &mt);
+}
+
+gboolean tape_bsf(int fd, guint count) {
+ struct mtop mt;
+ mt.mt_op = MTBSF;
+ mt.mt_count = count;
+ return 0 == ioctl(fd, MTIOCTOP, &mt);
+}
+
+gboolean tape_fsr(int fd, guint count) {
+ struct mtop mt;
+ mt.mt_op = MTFSR;
+ mt.mt_count = count;
+ return 0 == ioctl(fd, MTIOCTOP, &mt);
+}
+
+gboolean tape_bsr(int fd, guint count) {
+ struct mtop mt;
+ mt.mt_op = MTBSR;
+ mt.mt_count = count;
+ return 0 == ioctl(fd, MTIOCTOP, &mt);
+}
+
+gint tape_fileno(int fd) {
+ struct mtget get;
+
+ if (0 != ioctl(fd, MTIOCGET, &get))
+ return TAPE_POSITION_UNKNOWN;
+ if (get.mt_fileno < 0)
+ return TAPE_POSITION_UNKNOWN;
+ else
+ return get.mt_fileno;
+}
+
+gint tape_eod(int fd) {
+ struct mtop mt;
+ struct mtget get;
+ mt.mt_op = MTEOM;
+ mt.mt_count = 1;
+ if (0 != ioctl(fd, MTIOCTOP, &mt))
+ return TAPE_OP_ERROR;
+
+ /* Ignored result. This is just to flush buffers. */
+ mt.mt_op = MTNOP;
+ ioctl(fd, MTIOCTOP, &mt);
+
+ if (0 != ioctl(fd, MTIOCGET, &get))
+ return TAPE_POSITION_UNKNOWN;
+ if (get.mt_fileno < 0)
+ return TAPE_POSITION_UNKNOWN;
+ else
+ return get.mt_fileno;
+}
+
+gboolean tape_weof(int fd, guint8 count) {
+ struct mtop mt;
+ mt.mt_op = MTWEOF;
+ mt.mt_count = count;
+ return 0 == ioctl(fd, MTIOCTOP, &mt);
+}
+
+gboolean tape_setcompression(int fd G_GNUC_UNUSED,
+ gboolean on G_GNUC_UNUSED) {
+#ifdef MTCOMPRESSION
+ struct mtop mt;
+ mt.mt_op = MTCOMPRESSION;
+ mt.mt_count = on;
+ return 0 == ioctl(fd, MTIOCTOP, &mt);
+#else
+ return 0;
+#endif
+}
+
+gboolean tape_offl(int fd) {
+ struct mtop mt;
+ int safe_errno;
+
+ mt.mt_op = MTOFFL;
+ mt.mt_count = 1;
+ if (0 == ioctl(fd, MTIOCTOP, &mt))
+ return TRUE;
+
+ safe_errno = errno;
+ g_debug("tape_off: ioctl(MTIOCTOP/MTOFFL) failed: %s", strerror(errno));
+ errno = safe_errno;
+
+ return FALSE;
+}
+
+DeviceStatusFlags tape_is_tape_device(int fd) {
+ struct mtop mt;
+ mt.mt_op = MTNOP;
+ mt.mt_count = 1;
+ if (0 == ioctl(fd, MTIOCTOP, &mt)) {
+ return DEVICE_STATUS_SUCCESS;
+#ifdef ENOMEDIUM
+ } else if (errno == ENOMEDIUM) {
+ return DEVICE_STATUS_VOLUME_MISSING;
+#endif
+ } else {
+ g_debug("tape_is_tape_device: ioctl(MTIOCTOP/MTNOP) failed: %s",
+ strerror(errno));
+ if (errno == EIO) {
+ /* some devices return EIO while the drive is busy loading */
+ return DEVICE_STATUS_DEVICE_ERROR|DEVICE_STATUS_DEVICE_BUSY;
+ } else {
+ return DEVICE_STATUS_DEVICE_ERROR;
+ }
+ }
+}
+
+DeviceStatusFlags tape_is_ready(int fd, TapeDevice *t_self G_GNUC_UNUSED) {
+ struct mtget get;
+ if (0 == ioctl(fd, MTIOCGET, &get)) {
+#if defined(GMT_ONLINE) || defined(GMT_DR_OPEN)
+ if (1
+#ifdef GMT_ONLINE
+ && (t_self->broken_gmt_online || GMT_ONLINE(get.mt_gstat))
+#endif
+#ifdef GMT_DR_OPEN
+ && !GMT_DR_OPEN(get.mt_gstat)
+#endif
+ ) {
+ return DEVICE_STATUS_SUCCESS;
+ } else {
+ return DEVICE_STATUS_VOLUME_MISSING;
+ }
+#else /* Neither macro is defined. */
+ return DEVICE_STATUS_SUCCESS;
+#endif
+ } else {
+ return DEVICE_STATUS_VOLUME_ERROR;
+ }
+}
+