📄 rait-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 *//* The RAIT device encapsulates some number of other devices into a single * redundant device. */#include "rait-device.h"#include <amanda.h>#include "property.h"#include <util.h>typedef enum { RAIT_STATUS_COMPLETE, /* All subdevices OK. */ RAIT_STATUS_DEGRADED, /* One subdevice failed. */ RAIT_STATUS_FAILED /* Two or more subdevices failed. */} RaitStatus;struct RaitDevicePrivate_s { GPtrArray * children; /* These flags are only relevant for reading. */ RaitStatus status; /* If status == RAIT_STATUS_DEGRADED, this holds the index of the failed node. It holds a negative number otherwise. */ int failed; guint block_size;};#define PRIVATE(o) (o->private)/* here are local prototypes */static void rait_device_init (RaitDevice * o);static void rait_device_class_init (RaitDeviceClass * c);static gboolean rait_device_open_device (Device * self, char * device_name);static gboolean rait_device_start (Device * self, DeviceAccessMode mode, char * label, char * timestamp);static gboolean rait_device_start_file(Device * self, const dumpfile_t * info);static gboolean rait_device_write_block (Device * self, guint size, gpointer data, gboolean last_block);static gboolean rait_device_finish_file (Device * self);static dumpfile_t * rait_device_seek_file (Device * self, guint file);static gboolean rait_device_seek_block (Device * self, guint64 block);static int rait_device_read_block (Device * self, gpointer buf, int * size);static gboolean rait_device_property_get (Device * self, DevicePropertyId id, GValue * val);static gboolean rait_device_property_set (Device * self, DevicePropertyId id, GValue * val);static gboolean rait_device_recycle_file (Device * self, guint filenum);static gboolean rait_device_finish (Device * self);static ReadLabelStatusFlags rait_device_read_label(Device * dself);static void find_simple_params(RaitDevice * self, guint * num_children, guint * data_children, int * blocksize);/* pointer to the class of our parent */static DeviceClass *parent_class = NULL;/* This function is replicated here in case we have GLib from before 2.4. * It should probably go eventually. */#if !GLIB_CHECK_VERSION(2,4,0)static voidg_ptr_array_foreach (GPtrArray *array, GFunc func, gpointer user_data){ guint i; g_return_if_fail (array); for (i = 0; i < array->len; i++) (*func) (array->pdata[i], user_data);}#endifGTyperait_device_get_type (void){ static GType type = 0; if G_UNLIKELY(type == 0) { static const GTypeInfo info = { sizeof (RaitDeviceClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) rait_device_class_init, (GClassFinalizeFunc) NULL, NULL /* class_data */, sizeof (RaitDevice), 0 /* n_preallocs */, (GInstanceInitFunc) rait_device_init, NULL }; type = g_type_register_static (TYPE_DEVICE, "RaitDevice", &info, (GTypeFlags)0); } return type;}static void g_object_unref_foreach(gpointer data, gpointer user_data G_GNUC_UNUSED) { g_return_if_fail(G_IS_OBJECT(data)); g_object_unref(data);}static voidrait_device_finalize(GObject *obj_self){ RaitDevice *self G_GNUC_UNUSED = RAIT_DEVICE (obj_self); if(G_OBJECT_CLASS(parent_class)->finalize) \ (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self); if(self->private->children) { g_ptr_array_foreach(self->private->children, g_object_unref_foreach, NULL); g_ptr_array_free (self->private->children, TRUE); self->private->children = NULL; } amfree(self->private);}static void rait_device_init (RaitDevice * o G_GNUC_UNUSED){ PRIVATE(o) = malloc(sizeof(RaitDevicePrivate)); PRIVATE(o)->children = g_ptr_array_new(); PRIVATE(o)->status = RAIT_STATUS_COMPLETE; PRIVATE(o)->failed = -1;}static void rait_device_class_init (RaitDeviceClass * c G_GNUC_UNUSED){ GObjectClass *g_object_class G_GNUC_UNUSED = (GObjectClass*) c; DeviceClass *device_class = (DeviceClass *)c; parent_class = g_type_class_ref (TYPE_DEVICE); device_class->open_device = rait_device_open_device; device_class->start = rait_device_start; device_class->start_file = rait_device_start_file; device_class->write_block = rait_device_write_block; device_class->finish_file = rait_device_finish_file; device_class->seek_file = rait_device_seek_file; device_class->seek_block = rait_device_seek_block; device_class->read_block = rait_device_read_block; device_class->property_get = rait_device_property_get; device_class->property_set = rait_device_property_set; device_class->recycle_file = rait_device_recycle_file; device_class->finish = rait_device_finish; device_class->read_label = rait_device_read_label; g_object_class->finalize = rait_device_finalize; g_thread_pool_set_max_unused_threads(-1);}/* This function does something a little clever and a little * complicated. It takes an array of operations and runs the given * function on each element in the array. The trick is that it runs them * all in parallel, in different threads. This is more efficient than it * sounds because we use a GThreadPool, which means calling this function * will probably not start any new threads at all, but rather use * existing ones. The func is called with two gpointer arguments: The * first from the array, the second is the data argument. * * When it returns, all the operations have been successfully * executed. If you want results from your operations, do it yourself * through the array. */static void do_thread_pool_op(GFunc func, GPtrArray * ops, gpointer data) { GThreadPool * pool; guint i; pool = g_thread_pool_new(func, data, -1, FALSE, NULL); for (i = 0; i < ops->len; i ++) { g_thread_pool_push(pool, g_ptr_array_index(ops, i), NULL); } g_thread_pool_free(pool, FALSE, TRUE);}/* This does the above, in a serial fashion (and without using threads) */static void do_unthreaded_ops(GFunc func, GPtrArray * ops, gpointer data G_GNUC_UNUSED) { guint i; for (i = 0; i < ops->len; i ++) { func(g_ptr_array_index(ops, i), NULL); }}/* This is the one that code below should call. It switches automatically between do_thread_pool_op and do_unthreaded_ops, depending on g_thread_supported(). */static void do_rait_child_ops(GFunc func, GPtrArray * ops, gpointer data) { if (g_thread_supported()) { do_thread_pool_op(func, ops, data); } else { do_unthreaded_ops(func, ops, data); }}/* Take a text string user_name, and break it out into an argv-style array of strings. For example, {foo,{bar,baz},bat} would return the strings "foo", "{bar,baz}", "bat", and NULL. Returns NULL on error. */static char ** parse_device_name(char * user_name) { GPtrArray * rval; char * cur_end = user_name; char * cur_begin = user_name; rval = g_ptr_array_new(); /* Check opening brace. */ if (*cur_begin != '{') return NULL; cur_begin ++; cur_end = cur_begin; for (;;) { switch (*cur_end) { case ',': { g_ptr_array_add(rval, g_strndup(cur_begin, cur_end - cur_begin)); cur_end ++; cur_begin = cur_end; continue; } case '{': /* We read until the matching closing brace. */ while (*cur_end != '}' && *cur_end != '\0') cur_end ++; if (*cur_end == '}') cur_end ++; continue; case '}': g_ptr_array_add(rval, g_strndup(cur_begin, cur_end - cur_begin)); goto OUTER_END; /* break loop, not switch */ case '\0': /* Unexpected NULL; abort. */ g_fprintf(stderr, "Invalid RAIT device name %s\n", user_name); g_ptr_array_free_full(rval); return NULL; default: cur_end ++; continue; } g_assert_not_reached(); } OUTER_END: if (cur_end[1] != '\0') { g_fprintf(stderr, "Invalid RAIT device name %s\n", user_name); g_ptr_array_free_full(rval); return NULL; } g_ptr_array_add(rval, NULL); return (char**) g_ptr_array_free(rval, FALSE);}/* Find a workable block size. */static gboolean find_block_size(RaitDevice * self) { uint min = 0; uint max = G_MAXUINT; uint result; GValue val; gboolean rval; guint i; guint data_children; for (i = 0; i < self->private->children->len; i ++) { uint child_min, child_max; GValue property_result; bzero(&property_result, sizeof(property_result)); if (!device_property_get(g_ptr_array_index(self->private->children, i), PROPERTY_MIN_BLOCK_SIZE, &property_result)) return FALSE; child_min = g_value_get_uint(&property_result); g_return_val_if_fail(child_min > 0, FALSE); if (!device_property_get(g_ptr_array_index(self->private->children, i), PROPERTY_MAX_BLOCK_SIZE, &property_result)) return FALSE; child_max = g_value_get_uint(&property_result); g_return_val_if_fail(child_max > 0, FALSE); if (child_min > max || child_max < min || child_min == 0) { return FALSE; } else { min = MAX(min, child_min); max = MIN(max, child_max); } } /* Now pick a number. */ g_assert(min <= max); if (max < MAX_TAPE_BLOCK_BYTES) result = max; else if (min > MAX_TAPE_BLOCK_BYTES) result = min; else result = MAX_TAPE_BLOCK_BYTES; /* User reads and writes bigger blocks. */ find_simple_params(self, NULL, &data_children, NULL); self->private->block_size = result * data_children; bzero(&val, sizeof(val)); g_value_init(&val, G_TYPE_INT); g_value_set_int(&val, result); /* We can't do device_property_set because it's disallowed according to the registered property base. */ rval = rait_device_property_set(DEVICE(self), PROPERTY_BLOCK_SIZE, &val); g_value_unset(&val); return rval;}/* Register properties that belong to the RAIT device proper, and not to subdevices. */static void register_rait_properties(RaitDevice * self) { Device * o = DEVICE(self); DeviceProperty prop; prop.access = PROPERTY_ACCESS_GET_MASK; prop.base = &device_property_min_block_size; device_add_property(o, &prop, NULL); prop.base = &device_property_max_block_size; device_add_property(o, &prop, NULL); prop.base = &device_property_block_size; device_add_property(o, &prop, NULL); prop.base = &device_property_canonical_name; device_add_property(o, &prop, NULL);}static void property_hash_union(GHashTable * properties, DeviceProperty * prop) { PropertyAccessFlags before, after; gpointer tmp; gboolean found; found = g_hash_table_lookup_extended(properties, GUINT_TO_POINTER(prop->base->ID), NULL, &tmp); before = GPOINTER_TO_UINT(tmp); if (!found) { after = prop->access; } else { after = before & prop->access; } g_hash_table_insert(properties, GUINT_TO_POINTER(prop->base->ID), GUINT_TO_POINTER(after));}/* A GHRFunc. */static gboolean zero_value(gpointer key G_GNUC_UNUSED, gpointer value, gpointer user_data G_GNUC_UNUSED) { return (0 == GPOINTER_TO_UINT(value));}/* A GHFunc */static void register_property_hash(gpointer key, gpointer value, gpointer user_data) { DevicePropertyId id = GPOINTER_TO_UINT(key); DeviceProperty prop; Device * device = (Device*)user_data; g_assert(IS_DEVICE(device)); prop.access = GPOINTER_TO_UINT(value); prop.base = device_property_get_by_id(id); device_add_property(device, &prop, NULL);}/* This function figures out which properties exist for all children, and * exports the unioned access mask. */static void register_properties(RaitDevice * self) { GHashTable * properties; /* PropertyID => PropertyAccessFlags */ guint j; properties = g_hash_table_new(g_direct_hash, g_direct_equal); /* Iterate the device list, find all properties. */ for (j = 0; j < self->private->children->len; j ++) { int i; Device * child = g_ptr_array_index(self->private->children, j); const DeviceProperty* device_property_list; device_property_list = device_property_get_list(child); for (i = 0; device_property_list[i].base != NULL; i ++) { property_hash_union(properties, (gpointer)&(device_property_list[i])); } } /* Then toss properties that can't be accessed. */ g_hash_table_foreach_remove(properties, zero_value, NULL); g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_BLOCK_SIZE)); g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_MIN_BLOCK_SIZE)); g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_MAX_BLOCK_SIZE)); g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_CANONICAL_NAME)); /* Finally, register the lot. */ g_hash_table_foreach(properties, register_property_hash, self); g_hash_table_destroy(properties); /* Then we have some of our own properties to register. */ register_rait_properties(self);}/* This structure contains common fields for many operations. Not all operations use all fields, however. */typedef struct { gpointer result; /* May be a pointer; may be an integer or boolean stored with GINT_TO_POINTER. */ Device * child; /* The device in question. Used by all operations. */ guint child_index; /* For recoverable operations (read-related operations), this field provides the number of this child in the self->private->children array. */} GenericOp;typedef gboolean (*BooleanExtractor)(gpointer data);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -