📄 tape-device.c
字号:
/* * Copyright (c) 2005 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 * 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., 505 N Mathlida Ave, Suite 120 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com */#include <string.h> /* memset() */#include "util.h"#include "tape-device.h"#include "tape-ops.h"/* 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)struct TapeDevicePrivate_s { /* This holds the total number of bytes written to the device, modulus RESETOFS_THRESHOLD. */ int write_count;};/* Possible (abstracted) results from a system I/O operation. */typedef enum { RESULT_SUCCESS, RESULT_ERROR, /* Undefined error. */ RESULT_SMALL_BUFFER, /* Tried to read with a buffer that is too small. */ RESULT_NO_DATA, /* End of File, while reading */ RESULT_NO_SPACE, /* Out of space. Sometimes we don't know if it was this or I/O error, but this is the preferred explanation. */ RESULT_MAX} IoResult;/* here are local prototypes */static void tape_device_init (TapeDevice * o);static void tape_device_class_init (TapeDeviceClass * c);static gboolean tape_device_open_device (Device * self, char * device_name);static ReadLabelStatusFlags tape_device_read_label(Device * self);static gboolean tape_device_write_block(Device * self, guint size, gpointer data, gboolean short_block);static gboolean tape_device_read_block(Device * self, gpointer buf, int * size_req);static gboolean tape_device_start (Device * self, DeviceAccessMode mode, char * label, char * timestamp);static gboolean tape_device_start_file (Device * self, const dumpfile_t * ji);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_property_get (Device * self, DevicePropertyId id, GValue * val);static gboolean tape_device_property_set (Device * self, DevicePropertyId id, GValue * val);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);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);/* pointer to the class of our parent */static DeviceClass *parent_class = NULL;GType tape_device_get_type (void){ static GType type = 0; if G_UNLIKELY(type == 0) { static const GTypeInfo info = { sizeof (TapeDeviceClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) tape_device_class_init, (GClassFinalizeFunc) NULL, NULL /* class_data */, sizeof (TapeDevice), 0 /* n_preallocs */, (GInstanceInitFunc) tape_device_init, NULL }; type = g_type_register_static (TYPE_DEVICE, "TapeDevice", &info, (GTypeFlags)0); } return type;}static void tape_device_init (TapeDevice * self) { Device * device_self; DeviceProperty prop; GValue response; device_self = (Device*)self; bzero(&response, sizeof(response)); self->private = malloc(sizeof(TapeDevicePrivate)); /* Clear all fields. */ self->min_block_size = self->fixed_block_size = 32768; self->max_block_size = self->read_block_size = MAX_TAPE_BLOCK_BYTES; self->fd = -1; self->fsf = self->bsf = self->fsr = self->bsr = self->eom = self->bsf_after_eom = self->compression = self->first_file = 0; self->final_filemarks = 2; self->private->write_count = 0; /* Register properites */ prop.base = &device_property_concurrency; prop.access = PROPERTY_ACCESS_GET_MASK; g_value_init(&response, CONCURRENCY_PARADIGM_TYPE); g_value_set_enum(&response, CONCURRENCY_PARADIGM_EXCLUSIVE); device_add_property(device_self, &prop, &response); g_value_unset(&response); prop.base = &device_property_streaming; g_value_init(&response, STREAMING_REQUIREMENT_TYPE); g_value_set_enum(&response, STREAMING_REQUIREMENT_DESIRED); device_add_property(device_self, &prop, &response); g_value_unset(&response); prop.base = &device_property_appendable; g_value_init(&response, G_TYPE_BOOLEAN); g_value_set_boolean(&response, TRUE); device_add_property(device_self, &prop, &response); prop.base = &device_property_partial_deletion; g_value_set_boolean(&response, FALSE); device_add_property(device_self, &prop, &response); g_value_unset(&response); prop.base = &device_property_medium_access_type; g_value_init(&response, MEDIA_ACCESS_MODE_TYPE); g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE); device_add_property(device_self, &prop, &response); g_value_unset(&response); prop.access = PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK; prop.base = &device_property_compression; device_add_property(device_self, &prop, NULL); prop.access = PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START; prop.base = &device_property_min_block_size; device_add_property(device_self, &prop, NULL); prop.base = &device_property_max_block_size; device_add_property(device_self, &prop, NULL); prop.base = &device_property_block_size; device_add_property(device_self, &prop, NULL); prop.base = &device_property_fsf; device_add_property(device_self, &prop, NULL); prop.base = &device_property_bsf; device_add_property(device_self, &prop, NULL); prop.base = &device_property_fsr; device_add_property(device_self, &prop, NULL); prop.base = &device_property_bsr; device_add_property(device_self, &prop, NULL); prop.base = &device_property_eom; device_add_property(device_self, &prop, NULL); prop.base = &device_property_bsf_after_eom; device_add_property(device_self, &prop, NULL); prop.base = &device_property_final_filemarks; device_add_property(device_self, &prop, NULL); prop.access = PROPERTY_ACCESS_GET_MASK; prop.base = &device_property_canonical_name; device_add_property(device_self, &prop, NULL);}static void tape_device_finalize(GObject * obj_self) { TapeDevice * self = TAPE_DEVICE(obj_self); if(G_OBJECT_CLASS(parent_class)->finalize) \ (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self); robust_close(self->fd); self->fd = -1; amfree(self->private);}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->read_block = tape_device_read_block; device_class->start = tape_device_start; device_class->start_file = tape_device_start_file; device_class->seek_file = tape_device_seek_file; device_class->seek_block = tape_device_seek_block; device_class->property_get = tape_device_property_get; device_class->property_set = tape_device_property_set; device_class->finish = tape_device_finish; g_object_class->finalize = tape_device_finalize;}void tape_device_register(void) { static const char * device_prefix_list[] = { "tape", NULL }; register_device(tape_device_factory, device_prefix_list);}#ifdef O_NONBLOCK/* Open the tape device, trying various combinations of O_RDWR and O_NONBLOCK. */static int try_open_tape_device(TapeDevice * self, char * device_name) { int rval; rval = robust_open(device_name, O_RDWR | O_NONBLOCK, 0); if (rval < 0 && (errno == EWOULDBLOCK || errno == EINVAL)) { /* Maybe we don't support O_NONBLOCK for tape devices. */ rval = robust_open(device_name, O_RDWR, 0); } if (rval >= 0) { self->write_open_errno = 0; } else { if (errno == EACCES || errno == EPERM) { /* Device is write-protected. */ self->write_open_errno = errno; rval = robust_open(device_name, O_RDONLY | O_NONBLOCK, 0); if (rval < 0 && (errno == EWOULDBLOCK || errno == EINVAL)) { rval = robust_open(device_name, O_RDONLY, 0); } } } /* Clear O_NONBLOCK for operations from now on. */ fcntl(rval, F_SETFL, fcntl(rval, F_GETFL, 0) & ~O_NONBLOCK); return rval;}#else /* !defined(O_NONBLOCK) */static int try_open_tape_device(TapeDevice * self, char * device_name) { int rval; rval = robust_open(device_name, O_RDWR); if (rval >= 0) { self->write_open_errno = 0; } else { if (errno == EACCES || errno == EPERM) { /* Device is write-protected. */ self->write_open_errno = errno; rval = robust_open(device_name, O_RDONLY); } } return rval;}#endif /* O_NONBLOCK */static gboolean tape_device_open_device (Device * d_self, char * device_name) { TapeDevice * self; self = TAPE_DEVICE(d_self); g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (device_name != NULL, FALSE); self->fd = try_open_tape_device(self, device_name); if (self->fd < 0) { g_fprintf(stderr, "Can't open tape device %s: %s\n", device_name, strerror(errno)); return FALSE; } /* Check that this is actually a tape device. */ if (tape_is_tape_device(self->fd) == TAPE_CHECK_FAILURE) { g_fprintf(stderr, "File %s is not a tape device.\n", device_name); robust_close(self->fd); return FALSE; } if (tape_is_ready(self->fd) == TAPE_CHECK_FAILURE) { g_fprintf(stderr, "Tape device %s is not ready or is empty.\n", device_name); robust_close(self->fd); return FALSE; } /* Rewind it. */ if (!tape_rewind(self->fd)) { g_fprintf(stderr, "Error rewinding device %s\n", device_name); robust_close(self->fd); return FALSE; } /* Get tape drive/OS info */ tape_device_discover_capabilities(self); /* And verify the above. */ g_assert(feature_support_flags_is_valid(self->fsf)); g_assert(feature_support_flags_is_valid(self->bsf)); g_assert(feature_support_flags_is_valid(self->fsr)); g_assert(feature_support_flags_is_valid(self->bsr)); g_assert(feature_support_flags_is_valid(self->eom)); g_assert(feature_support_flags_is_valid(self->bsf_after_eom)); g_assert(self->final_filemarks == 1 || self->final_filemarks == 2); /* Chain up */ if (parent_class->open_device) { if (!(parent_class->open_device)(d_self, device_name)) { robust_close(self->fd); return FALSE; } } return TRUE;}static ReadLabelStatusFlags tape_device_read_label(Device * dself) { TapeDevice * self; char * header_buffer; int buffer_len; IoResult result; dumpfile_t header; self = TAPE_DEVICE(dself); g_return_val_if_fail(self != NULL, FALSE); if (!tape_rewind(self->fd)) { g_fprintf(stderr, "Error rewinding device %s\n", dself->device_name); return (READ_LABEL_STATUS_DEVICE_ERROR | READ_LABEL_STATUS_VOLUME_ERROR); } buffer_len = self->read_block_size; header_buffer = malloc(buffer_len); result = tape_device_robust_read(self, header_buffer, &buffer_len); if (result != RESULT_SUCCESS) { free(header_buffer); tape_rewind(self->fd); /* I/O error. */ g_fprintf(stderr, "Error reading Amanda header.\n"); if (result == RESULT_NO_DATA) { return (READ_LABEL_STATUS_VOLUME_ERROR | READ_LABEL_STATUS_VOLUME_UNLABELED); } else { return (READ_LABEL_STATUS_DEVICE_ERROR | READ_LABEL_STATUS_VOLUME_ERROR | READ_LABEL_STATUS_VOLUME_UNLABELED); } } parse_file_header(header_buffer, &header, buffer_len); amfree(header_buffer); if (header.type != F_TAPESTART) { return READ_LABEL_STATUS_VOLUME_UNLABELED; } dself->volume_label = g_strdup(header.name); dself->volume_time = g_strdup(header.datestamp); if (parent_class->read_label) { return parent_class->read_label(dself); } else { return READ_LABEL_STATUS_SUCCESS; }}static gbooleantape_device_write_block(Device * pself, guint size, gpointer data, gboolean short_block) { TapeDevice * self; char *replacement_buffer = NULL; IoResult result; self = TAPE_DEVICE(pself); g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (self->fd >= 0, FALSE); if (short_block && self->min_block_size > size) { replacement_buffer = malloc(self->min_block_size); memcpy(replacement_buffer, data, size); bzero(replacement_buffer+size, self->min_block_size-size); data = replacement_buffer; size = self->min_block_size; } result = tape_device_robust_write(self, data, size); if (result == RESULT_SUCCESS) { if (parent_class->write_block) { (parent_class->write_block)(pself, size, data, short_block); } amfree(replacement_buffer); return TRUE; } else { amfree(replacement_buffer); return FALSE; } g_assert_not_reached();}static int tape_device_read_block (Device * pself, gpointer buf, int * size_req) { TapeDevice * self; int size; IoResult result; self = TAPE_DEVICE(pself); g_return_val_if_fail (self != NULL, -1); if (buf == NULL || *size_req < (int)self->read_block_size) { /* Just a size query. */ *size_req = self->read_block_size; return 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -