2 * Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
21 /* The RAIT device encapsulates some number of other devices into a single
22 * redundant device. */
28 #include "glib-util.h"
30 #include "fileheader.h"
31 #include "amsemaphore.h"
33 /* Just a note about the failure mode of different operations:
34 - Recovers from a failure (enters degraded mode)
36 seek_file() -- explodes if headers don't match.
37 seek_block() -- explodes if headers don't match.
38 read_block() -- explodes if data doesn't match.
40 - Operates in degraded mode (but dies if a new problem shows up)
41 read_label() -- but dies on label mismatch.
42 start() -- but dies when writing in degraded mode.
46 - Dies in degraded mode (even if remaining devices are OK)
54 * Type checking and casting macros
56 #define TYPE_RAIT_DEVICE (rait_device_get_type())
57 #define RAIT_DEVICE(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), rait_device_get_type(), RaitDevice)
58 #define RAIT_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), rait_device_get_type(), RaitDevice const)
59 #define RAIT_DEVICE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), rait_device_get_type(), RaitDeviceClass)
60 #define IS_RAIT_DEVICE(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), rait_device_get_type ())
62 #define RAIT_DEVICE_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), rait_device_get_type(), RaitDeviceClass)
63 static GType rait_device_get_type (void);
66 * Main object structure
68 typedef struct RaitDevice_s {
71 struct RaitDevicePrivate_s * private;
77 typedef struct _RaitDeviceClass RaitDeviceClass;
78 struct _RaitDeviceClass {
79 DeviceClass __parent__;
83 RAIT_STATUS_COMPLETE, /* All subdevices OK. */
84 RAIT_STATUS_DEGRADED, /* One subdevice failed. */
85 RAIT_STATUS_FAILED /* Two or more subdevices failed. */
88 /* Older versions of glib have a deadlock in their thread pool implementations,
89 * so we include a simple thread-pool implementation here to replace it.
91 * This implementation assumes that threads are used for paralellizing a single
92 * operation, so all threads run a function to completion before the main thread
93 * continues. This simplifies some of the locking semantics, and in particular
94 * there is no need to wait for stray threads to finish an operation when
95 * finalizing the RaitDevice object or when beginning a new operation.
97 #if !(GLIB_CHECK_VERSION(2,10,0))
98 #define USE_INTERNAL_THREADPOOL
101 typedef struct RaitDevicePrivate_s {
102 GPtrArray * children;
103 /* These flags are only relevant for reading. */
105 /* If status == RAIT_STATUS_DEGRADED, this holds the index of the
106 failed node. It holds a negative number otherwise. */
109 /* the child block size */
110 gsize child_block_size;
112 #ifdef USE_INTERNAL_THREADPOOL
113 /* array of ThreadInfo for performing parallel operations */
116 /* value of this semaphore is the number of threaded operations
118 amsemaphore_t *threads_sem;
122 #ifdef USE_INTERNAL_THREADPOOL
123 typedef struct ThreadInfo {
126 /* struct fields below are protected by this mutex and condition variable */
134 /* give threads access to active_threads and its mutex/cond */
135 struct RaitDevicePrivate_s *private;
139 /* This device uses a special sentinel node to indicate that the child devices
140 * will be set later (in rait_device_open). It contains a control character to
141 * make it difficult to enter accidentally in an Amanda config. */
142 #define DEFER_CHILDREN_SENTINEL "DEFER\1"
144 #define PRIVATE(o) (o->private)
146 #define rait_device_in_error(dev) \
147 (device_in_error((dev)) || PRIVATE(RAIT_DEVICE((dev)))->status == RAIT_STATUS_FAILED)
149 void rait_device_register (void);
151 /* here are local prototypes */
152 static void rait_device_init (RaitDevice * o);
153 static void rait_device_class_init (RaitDeviceClass * c);
154 static void rait_device_base_init (RaitDeviceClass * c);
155 static void rait_device_open_device (Device * self, char * device_name, char * device_type, char * device_node);
156 static gboolean rait_device_start (Device * self, DeviceAccessMode mode,
157 char * label, char * timestamp);
158 static gboolean rait_device_configure(Device * self, gboolean use_global_config);
159 static gboolean rait_device_start_file(Device * self, dumpfile_t * info);
160 static gboolean rait_device_write_block (Device * self, guint size, gpointer data);
161 static gboolean rait_device_finish_file (Device * self);
162 static dumpfile_t * rait_device_seek_file (Device * self, guint file);
163 static gboolean rait_device_seek_block (Device * self, guint64 block);
164 static int rait_device_read_block (Device * self, gpointer buf,
166 static gboolean rait_device_recycle_file (Device * self, guint filenum);
167 static gboolean rait_device_finish (Device * self);
168 static DeviceStatusFlags rait_device_read_label(Device * dself);
169 static void find_simple_params(RaitDevice * self, guint * num_children,
170 guint * data_children);
172 /* property handlers */
174 static gboolean property_get_block_size_fn(Device *self,
175 DevicePropertyBase *base, GValue *val,
176 PropertySurety *surety, PropertySource *source);
178 static gboolean property_set_block_size_fn(Device *self,
179 DevicePropertyBase *base, GValue *val,
180 PropertySurety surety, PropertySource source);
182 static gboolean property_get_canonical_name_fn(Device *self,
183 DevicePropertyBase *base, GValue *val,
184 PropertySurety *surety, PropertySource *source);
186 static gboolean property_get_concurrency_fn(Device *self,
187 DevicePropertyBase *base, GValue *val,
188 PropertySurety *surety, PropertySource *source);
190 static gboolean property_get_streaming_fn(Device *self,
191 DevicePropertyBase *base, GValue *val,
192 PropertySurety *surety, PropertySource *source);
194 static gboolean property_get_boolean_and_fn(Device *self,
195 DevicePropertyBase *base, GValue *val,
196 PropertySurety *surety, PropertySource *source);
198 static gboolean property_get_medium_access_type_fn(Device *self,
199 DevicePropertyBase *base, GValue *val,
200 PropertySurety *surety, PropertySource *source);
202 static gboolean property_get_max_volume_usage_fn(Device *self,
203 DevicePropertyBase *base, GValue *val,
204 PropertySurety *surety, PropertySource *source);
206 static gboolean property_set_max_volume_usage_fn(Device *self,
207 DevicePropertyBase *base, GValue *val,
208 PropertySurety surety, PropertySource source);
211 /* pointer to the class of our parent */
212 static DeviceClass *parent_class = NULL;
215 rait_device_get_type (void)
217 static GType type = 0;
219 if G_UNLIKELY(type == 0) {
220 static const GTypeInfo info = {
221 sizeof (RaitDeviceClass),
222 (GBaseInitFunc) rait_device_base_init,
223 (GBaseFinalizeFunc) NULL,
224 (GClassInitFunc) rait_device_class_init,
225 (GClassFinalizeFunc) NULL,
226 NULL /* class_data */,
229 (GInstanceInitFunc) rait_device_init,
233 type = g_type_register_static (TYPE_DEVICE, "RaitDevice", &info,
240 static void g_object_unref_foreach(gpointer data,
241 gpointer user_data G_GNUC_UNUSED) {
242 if (data != NULL && G_IS_OBJECT(data)) {
243 g_object_unref(data);
248 rait_device_finalize(GObject *obj_self)
250 RaitDevice *self = RAIT_DEVICE (obj_self);
251 if(G_OBJECT_CLASS(parent_class)->finalize) \
252 (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
253 if(self->private->children) {
254 g_ptr_array_foreach(self->private->children,
255 g_object_unref_foreach, NULL);
256 g_ptr_array_free (self->private->children, TRUE);
257 self->private->children = NULL;
259 #ifdef USE_INTERNAL_THREADPOOL
260 g_assert(PRIVATE(self)->threads_sem == NULL || PRIVATE(self)->threads_sem->value == 0);
262 if (PRIVATE(self)->threads) {
265 for (i = 0; i < PRIVATE(self)->threads->len; i++) {
266 ThreadInfo *inf = &g_array_index(PRIVATE(self)->threads, ThreadInfo, i);
268 /* NOTE: the thread is waiting on this condition right now, not
269 * executing an operation. */
271 /* ask the thread to die */
272 g_mutex_lock(inf->mutex);
274 g_cond_signal(inf->cond);
275 g_mutex_unlock(inf->mutex);
277 /* and wait for it to die, which should happen soon */
278 g_thread_join(inf->thread);
282 g_mutex_free(inf->mutex);
284 g_cond_free(inf->cond);
288 if (PRIVATE(self)->threads_sem)
289 amsemaphore_free(PRIVATE(self)->threads_sem);
291 amfree(self->private);
295 rait_device_init (RaitDevice * o G_GNUC_UNUSED)
297 PRIVATE(o) = g_new(RaitDevicePrivate, 1);
298 PRIVATE(o)->children = g_ptr_array_new();
299 PRIVATE(o)->status = RAIT_STATUS_COMPLETE;
300 PRIVATE(o)->failed = -1;
301 #ifdef USE_INTERNAL_THREADPOOL
302 PRIVATE(o)->threads = NULL;
303 PRIVATE(o)->threads_sem = NULL;
308 rait_device_class_init (RaitDeviceClass * c)
310 GObjectClass *g_object_class = (GObjectClass*) c;
311 DeviceClass *device_class = (DeviceClass *)c;
313 parent_class = g_type_class_ref (TYPE_DEVICE);
315 device_class->open_device = rait_device_open_device;
316 device_class->configure = rait_device_configure;
317 device_class->start = rait_device_start;
318 device_class->start_file = rait_device_start_file;
319 device_class->write_block = rait_device_write_block;
320 device_class->finish_file = rait_device_finish_file;
321 device_class->seek_file = rait_device_seek_file;
322 device_class->seek_block = rait_device_seek_block;
323 device_class->read_block = rait_device_read_block;
324 device_class->recycle_file = rait_device_recycle_file;
325 device_class->finish = rait_device_finish;
326 device_class->read_label = rait_device_read_label;
328 g_object_class->finalize = rait_device_finalize;
330 #ifndef USE_INTERNAL_THREADPOOL
331 #if !GLIB_CHECK_VERSION(2,10,2)
332 /* Versions of glib before 2.10.2 crash if
333 * g_thread_pool_set_max_unused_threads is called before the first
334 * invocation of g_thread_pool_new. So we make up a thread pool, but don't
335 * start any threads in it, and free it */
337 GThreadPool *pool = g_thread_pool_new((GFunc)-1, NULL, -1, FALSE, NULL);
338 g_thread_pool_free(pool, TRUE, FALSE);
342 g_thread_pool_set_max_unused_threads(-1);
347 rait_device_base_init (RaitDeviceClass * c)
349 DeviceClass *device_class = (DeviceClass *)c;
351 /* the RAIT device overrides most of the standard properties, so that it
352 * can calculate them by querying the same property on the children */
353 device_class_register_property(device_class, PROPERTY_BLOCK_SIZE,
354 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
355 property_get_block_size_fn,
356 property_set_block_size_fn);
358 device_class_register_property(device_class, PROPERTY_CANONICAL_NAME,
359 PROPERTY_ACCESS_GET_MASK,
360 property_get_canonical_name_fn, NULL);
362 device_class_register_property(device_class, PROPERTY_CONCURRENCY,
363 PROPERTY_ACCESS_GET_MASK,
364 property_get_concurrency_fn, NULL);
366 device_class_register_property(device_class, PROPERTY_STREAMING,
367 PROPERTY_ACCESS_GET_MASK,
368 property_get_streaming_fn, NULL);
370 device_class_register_property(device_class, PROPERTY_APPENDABLE,
371 PROPERTY_ACCESS_GET_MASK,
372 property_get_boolean_and_fn, NULL);
374 device_class_register_property(device_class, PROPERTY_PARTIAL_DELETION,
375 PROPERTY_ACCESS_GET_MASK,
376 property_get_boolean_and_fn, NULL);
378 device_class_register_property(device_class, PROPERTY_FULL_DELETION,
379 PROPERTY_ACCESS_GET_MASK,
380 property_get_boolean_and_fn, NULL);
382 device_class_register_property(device_class, PROPERTY_LEOM,
383 PROPERTY_ACCESS_GET_MASK,
384 property_get_boolean_and_fn, NULL);
386 device_class_register_property(device_class, PROPERTY_MEDIUM_ACCESS_TYPE,
387 PROPERTY_ACCESS_GET_MASK,
388 property_get_medium_access_type_fn, NULL);
390 device_class_register_property(device_class, PROPERTY_MAX_VOLUME_USAGE,
391 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
392 property_get_max_volume_usage_fn,
393 property_set_max_volume_usage_fn);
396 /* This function does something a little clever and a little
397 * complicated. It takes an array of operations and runs the given
398 * function on each element in the array. The trick is that it runs them
399 * all in parallel, in different threads. This is more efficient than it
400 * sounds because we use a GThreadPool, which means calling this function
401 * will probably not start any new threads at all, but rather use
402 * existing ones. The func is called with two gpointer arguments: The
403 * first from the array, the second is the data argument.
405 * When it returns, all the operations have been successfully
406 * executed. If you want results from your operations, do it yourself
410 #ifdef USE_INTERNAL_THREADPOOL
411 static gpointer rait_thread_pool_func(gpointer data) {
412 ThreadInfo *inf = data;
414 g_mutex_lock(inf->mutex);
416 while (!inf->die && !inf->func)
417 g_cond_wait(inf->cond, inf->mutex);
423 /* invoke the function */
424 inf->func(inf->data, NULL);
428 /* indicate that we're finished; will not block */
429 amsemaphore_down(inf->private->threads_sem);
432 g_mutex_unlock(inf->mutex);
436 static void do_thread_pool_op(RaitDevice *self, GFunc func, GPtrArray * ops) {
439 if (PRIVATE(self)->threads_sem == NULL)
440 PRIVATE(self)->threads_sem = amsemaphore_new_with_value(0);
442 if (PRIVATE(self)->threads == NULL)
443 PRIVATE(self)->threads = g_array_sized_new(FALSE, TRUE,
444 sizeof(ThreadInfo), ops->len);
446 g_assert(PRIVATE(self)->threads_sem->value == 0);
448 if (PRIVATE(self)->threads->len < ops->len)
449 g_array_set_size(PRIVATE(self)->threads, ops->len);
451 /* the semaphore will hit zero when each thread has decremented it */
452 amsemaphore_force_set(PRIVATE(self)->threads_sem, ops->len);
454 for (i = 0; i < ops->len; i++) {
455 ThreadInfo *inf = &g_array_index(PRIVATE(self)->threads, ThreadInfo, i);
457 inf->mutex = g_mutex_new();
458 inf->cond = g_cond_new();
459 inf->private = PRIVATE(self);
460 inf->thread = g_thread_create(rait_thread_pool_func, inf, TRUE, NULL);
463 /* set up the info the thread needs and trigger it to start */
464 g_mutex_lock(inf->mutex);
465 inf->data = g_ptr_array_index(ops, i);
467 g_cond_signal(inf->cond);
468 g_mutex_unlock(inf->mutex);
471 /* wait until semaphore hits zero */
472 amsemaphore_wait_empty(PRIVATE(self)->threads_sem);
475 #else /* USE_INTERNAL_THREADPOOL */
477 static void do_thread_pool_op(RaitDevice *self G_GNUC_UNUSED, GFunc func, GPtrArray * ops) {
481 pool = g_thread_pool_new(func, NULL, -1, FALSE, NULL);
482 for (i = 0; i < ops->len; i ++) {
483 g_thread_pool_push(pool, g_ptr_array_index(ops, i), NULL);
486 g_thread_pool_free(pool, FALSE, TRUE);
489 #endif /* USE_INTERNAL_THREADPOOL */
491 /* This does the above, in a serial fashion (and without using threads) */
492 static void do_unthreaded_ops(RaitDevice *self G_GNUC_UNUSED, GFunc func, GPtrArray * ops) {
495 for (i = 0; i < ops->len; i ++) {
496 func(g_ptr_array_index(ops, i), NULL);
500 /* This is the one that code below should call. It switches
501 automatically between do_thread_pool_op and do_unthreaded_ops,
502 depending on g_thread_supported(). */
503 static void do_rait_child_ops(RaitDevice *self, GFunc func, GPtrArray * ops) {
504 if (g_thread_supported()) {
505 do_thread_pool_op(self, func, ops);
507 do_unthreaded_ops(self, func, ops);
512 child_device_names_to_rait_name(RaitDevice * self) {
514 char *braced, *result;
517 kids = g_ptr_array_sized_new(self->private->children->len);
518 for (i = 0; i < self->private->children->len; i ++) {
519 Device *child = g_ptr_array_index(self->private->children, i);
520 const char *child_name = NULL;
522 gboolean got_prop = FALSE;
524 bzero(&val, sizeof(val));
526 if ((signed)i != self->private->failed) {
527 if (device_property_get(child, PROPERTY_CANONICAL_NAME, &val)) {
528 child_name = g_value_get_string(&val);
534 child_name = "MISSING";
536 g_ptr_array_add(kids, g_strdup(child_name));
542 braced = collapse_braced_alternates(kids);
543 result = g_strdup_printf("rait:%s", braced);
549 /* Find a workable child block size, based on the block size ranges of our
552 * The algorithm is to construct the intersection of all child devices'
553 * [min,max] block size ranges, and then pick the block size closest to 32k
554 * that is in the resulting range. This avoids picking ridiculously small (1
555 * byte) or large (INT_MAX) block sizes when using devices with wide-open block
558 * This function returns the calculated child block size directly, and the RAIT
559 * device's blocksize via rait_size, if not NULL. It is resilient to errors in
560 * a single child device, but sets the device's error status and returns 0 if
561 * it cannot determine an agreeable block size.
564 calculate_block_size_from_children(RaitDevice * self, gsize *rait_size)
567 gsize max = SIZE_MAX;
568 gboolean found_one = FALSE;
572 for (i = 0; i < self->private->children->len; i ++) {
573 gsize child_min = SIZE_MAX, child_max = 0;
575 GValue property_result;
576 PropertySource source;
578 bzero(&property_result, sizeof(property_result));
580 if ((signed)i == self->private->failed)
583 child = g_ptr_array_index(self->private->children, i);
584 if (!device_property_get_ex(child, PROPERTY_BLOCK_SIZE,
585 &property_result, NULL, &source)) {
586 g_warning("Error getting BLOCK_SIZE from %s: %s",
587 child->device_name, device_error_or_status(child));
591 /* if the block size has been set explicitly, then we need to use that blocksize;
592 * otherwise (even if it was DETECTED), override it. */
593 if (source == PROPERTY_SOURCE_USER) {
594 child_min = child_max = g_value_get_int(&property_result);
596 if (!device_property_get(child, PROPERTY_MIN_BLOCK_SIZE,
598 g_warning("Error getting MIN_BLOCK_SIZE from %s: %s",
599 child->device_name, device_error_or_status(child));
602 child_min = g_value_get_uint(&property_result);
604 if (!device_property_get(child, PROPERTY_MAX_BLOCK_SIZE,
606 g_warning("Error getting MAX_BLOCK_SIZE from %s: %s",
607 child->device_name, device_error_or_status(child));
610 child_max = g_value_get_uint(&property_result);
612 if (child_min == 0 || child_max == 0 || (child_min > child_max)) {
613 g_warning("Invalid min, max block sizes from %s", child->device_name);
619 min = MAX(min, child_min);
620 max = MIN(max, child_max);
624 device_set_error((Device*)self,
625 stralloc(_("Could not find any child devices' block size ranges")),
626 DEVICE_STATUS_DEVICE_ERROR);
631 device_set_error((Device*)self,
632 stralloc(_("No block size is acceptable to all child devices")),
633 DEVICE_STATUS_DEVICE_ERROR);
637 /* Now pick a number. If 32k is in range, we use that; otherwise, we use
638 * the nearest acceptable size. */
639 result = CLAMP(32768, min, max);
643 find_simple_params(self, NULL, &data_children);
644 *rait_size = result * data_children;
650 /* Set BLOCK_SIZE on all children */
652 set_block_size_on_children(RaitDevice *self, gsize child_block_size)
656 PropertySource source;
658 bzero(&val, sizeof(val));
660 g_assert(child_block_size < INT_MAX);
661 g_value_init(&val, G_TYPE_INT);
662 g_value_set_int(&val, (gint)child_block_size);
664 for (i = 0; i < self->private->children->len; i ++) {
666 GValue property_result;
668 bzero(&property_result, sizeof(property_result));
670 if ((signed)i == self->private->failed)
673 child = g_ptr_array_index(self->private->children, i);
675 /* first, make sure the block size is at its default, or is already
677 if (device_property_get_ex(child, PROPERTY_BLOCK_SIZE,
678 &property_result, NULL, &source)) {
679 gsize from_child = g_value_get_int(&property_result);
680 g_value_unset(&property_result);
681 if (source != PROPERTY_SOURCE_DEFAULT
682 && from_child != child_block_size) {
683 device_set_error((Device *)self,
684 vstrallocf(_("Child device %s already has its block size set to %zd, not %zd"),
685 child->device_name, from_child, child_block_size),
686 DEVICE_STATUS_DEVICE_ERROR);
690 /* failing to get the block size isn't necessarily fatal.. */
691 g_warning("Error getting BLOCK_SIZE from %s: %s",
692 child->device_name, device_error_or_status(child));
695 if (!device_property_set(child, PROPERTY_BLOCK_SIZE, &val)) {
696 device_set_error((Device *)self,
697 vstrallocf(_("Error setting block size on %s"), child->device_name),
698 DEVICE_STATUS_DEVICE_ERROR);
706 /* The time for users to specify block sizes has ended; set this device's
707 * block-size attributes for easy access by other RAIT functions. Returns
708 * FALSE on error, with the device's error status already set. */
710 fix_block_size(RaitDevice *self)
712 Device *dself = (Device *)self;
713 gsize my_block_size, child_block_size;
715 if (dself->block_size_source == PROPERTY_SOURCE_DEFAULT) {
716 child_block_size = calculate_block_size_from_children(self, &my_block_size);
717 if (child_block_size == 0)
720 self->private->child_block_size = child_block_size;
721 dself->block_size = my_block_size;
722 dself->block_size_surety = PROPERTY_SURETY_GOOD;
723 dself->block_size_source = PROPERTY_SOURCE_DETECTED;
727 find_simple_params(self, NULL, &data_children);
728 g_assert((dself->block_size % data_children) == 0);
729 child_block_size = dself->block_size / data_children;
732 /* now tell the children we mean it */
733 if (!set_block_size_on_children(self, child_block_size))
739 /* This structure contains common fields for many operations. Not all
740 operations use all fields, however. */
742 gpointer result; /* May be a pointer; may be an integer or boolean
743 stored with GINT_TO_POINTER. */
744 Device * child; /* The device in question. Used by all
746 guint child_index; /* For recoverable operations (read-related
747 operations), this field provides the number
748 of this child in the self->private->children
752 typedef gboolean (*BooleanExtractor)(gpointer data);
754 /* A BooleanExtractor */
755 static gboolean extract_boolean_generic_op(gpointer data) {
756 GenericOp * op = data;
757 return GPOINTER_TO_INT(op->result);
760 /* A BooleanExtractor */
761 static gboolean extract_boolean_pointer_op(gpointer data) {
762 GenericOp * op = data;
763 return op->result != NULL;
766 /* Does the equivalent of this perl command:
767 ! (first { !extractor($_) } @_
768 That is, calls extractor on each element of the array, and returns
769 TRUE if and only if all calls to extractor return TRUE. This function
770 stops as soon as an extractor returns false, so it's best if extractor
771 functions have no side effects.
773 static gboolean g_ptr_array_and(GPtrArray * array,
774 BooleanExtractor extractor) {
776 if (array == NULL || array->len <= 0)
779 for (i = 0; i < array->len; i ++) {
780 if (!extractor(g_ptr_array_index(array, i)))
787 /* Takes a RaitDevice, and makes a GPtrArray of GenericOp. */
788 static GPtrArray * make_generic_boolean_op_array(RaitDevice* self) {
792 rval = g_ptr_array_sized_new(self->private->children->len);
793 for (i = 0; i < self->private->children->len; i ++) {
796 if ((signed)i == self->private->failed) {
800 op = g_new(GenericOp, 1);
801 op->child = g_ptr_array_index(self->private->children, i);
803 g_ptr_array_add(rval, op);
809 /* Takes a GPtrArray of GenericOp, and a BooleanExtractor, and does
810 all the proper handling for the result of operations that allow
811 device isolation. Returns FALSE only if an unrecoverable error
813 static gboolean g_ptr_array_union_robust(RaitDevice * self, GPtrArray * ops,
814 BooleanExtractor extractor) {
819 /* We found one or more failed elements. See which elements failed, and
821 for (i = 0; i < ops->len; i ++) {
822 GenericOp * op = g_ptr_array_index(ops, i);
823 if (!extractor(op)) {
824 self->private->failed = op->child_index;
825 g_warning("RAIT array %s isolated device %s: %s",
826 DEVICE(self)->device_name,
827 op->child->device_name,
828 device_error(op->child));
834 /* no failures? great! */
838 /* a single failure in COMPLETE just puts us in DEGRADED mode */
839 if (self->private->status == RAIT_STATUS_COMPLETE && nfailed == 1) {
840 self->private->status = RAIT_STATUS_DEGRADED;
841 self->private->failed = lastfailed;
842 g_warning("RAIT array %s DEGRADED", DEVICE(self)->device_name);
845 self->private->status = RAIT_STATUS_FAILED;
846 g_warning("RAIT array %s FAILED", DEVICE(self)->device_name);
854 char * device_name; /* IN */
855 Device * result; /* OUT */
859 static void device_open_do_op(gpointer data,
860 gpointer user_data G_GNUC_UNUSED) {
861 OpenDeviceOp * op = data;
863 if (strcmp(op->device_name, "ERROR") == 0 ||
864 strcmp(op->device_name, "MISSING") == 0 ||
865 strcmp(op->device_name, "DEGRADED") == 0) {
866 g_warning("RAIT device %s contains a missing element, attempting "
867 "degraded mode.\n", op->rait_name);
870 op->result = device_open(op->device_name);
874 /* Returns TRUE if and only if the volume label and time are equal. */
875 static gboolean compare_volume_results(Device * a, Device * b) {
876 return (0 == compare_possibly_null_strings(a->volume_time, b->volume_time)
877 && 0 == compare_possibly_null_strings(a->volume_label, b->volume_label));
880 /* Stickes new_message at the end of *old_message; frees new_message and
881 * may change *old_message. */
882 static void append_message(char ** old_message, char * new_message) {
884 if (*old_message == NULL || **old_message == '\0') {
887 rval = g_strdup_printf("%s; %s", *old_message, new_message);
890 amfree(*old_message);
895 open_child_devices (Device * dself, char * device_name,
896 char * device_node) {
897 GPtrArray *device_names;
898 GPtrArray * device_open_ops;
901 char *failure_errmsgs;
902 DeviceStatusFlags failure_flags;
905 self = RAIT_DEVICE(dself);
907 device_names = expand_braced_alternates(device_node);
909 if (device_names == NULL) {
910 device_set_error(dself,
911 vstrallocf(_("Invalid RAIT device name '%s'"), device_name),
912 DEVICE_STATUS_DEVICE_ERROR);
916 /* Open devices in a separate thread, in case they have to rewind etc. */
917 device_open_ops = g_ptr_array_new();
919 for (i = 0; i < device_names->len; i++) {
921 char *name = g_ptr_array_index(device_names, i);
923 op = g_new(OpenDeviceOp, 1);
924 op->device_name = name;
927 op->rait_name = device_name;
928 g_ptr_array_add(device_open_ops, op);
931 g_ptr_array_free(device_names, TRUE);
932 do_rait_child_ops(self, device_open_do_op, device_open_ops);
935 failure_errmsgs = NULL;
938 /* Check results of opening devices. */
939 for (i = 0; i < device_open_ops->len; i ++) {
940 OpenDeviceOp *op = g_ptr_array_index(device_open_ops, i);
942 if (op->result != NULL &&
943 op->result->status == DEVICE_STATUS_SUCCESS) {
944 g_ptr_array_add(self->private->children, op->result);
946 char * this_failure_errmsg =
947 g_strdup_printf("%s: %s", op->device_name,
948 device_error_or_status(op->result));
949 DeviceStatusFlags status =
951 DEVICE_STATUS_DEVICE_ERROR : op->result->status;
952 append_message(&failure_errmsgs,
953 strdup(this_failure_errmsg));
954 failure_flags |= status;
955 if (self->private->status == RAIT_STATUS_COMPLETE) {
956 /* The first failure just puts us in degraded mode. */
958 device_name, this_failure_errmsg);
959 g_warning("%s: %s failed, entering degraded mode.",
960 device_name, op->device_name);
961 g_ptr_array_add(self->private->children, op->result);
962 self->private->status = RAIT_STATUS_DEGRADED;
963 self->private->failed = i;
965 /* The second and further failures are fatal. */
969 amfree(op->device_name);
972 g_ptr_array_free_full(device_open_ops);
975 self->private->status = RAIT_STATUS_FAILED;
976 device_set_error(dself, failure_errmsgs, failure_flags);
984 rait_device_open_device (Device * dself, char * device_name,
985 char * device_type G_GNUC_UNUSED, char * device_node) {
987 if (0 != strcmp(device_node, DEFER_CHILDREN_SENTINEL)) {
988 if (!open_child_devices(dself, device_name, device_node))
992 if (parent_class->open_device) {
993 parent_class->open_device(dself, device_name, device_type, device_node);
999 rait_device_open_from_children (GSList *child_devices) {
1007 /* first, open a RAIT device using the DEFER_CHILDREN_SENTINEL */
1008 dself = device_open("rait:" DEFER_CHILDREN_SENTINEL);
1009 if (!IS_RAIT_DEVICE(dself)) {
1013 /* set its children */
1014 self = RAIT_DEVICE(dself);
1016 for (i=0, iter = child_devices; iter; i++, iter = iter->next) {
1017 Device *kid = iter->data;
1019 /* a NULL kid is OK -- it opens the device in degraded mode */
1022 self->private->failed = i;
1024 g_assert(IS_DEVICE(kid));
1025 g_object_ref((GObject *)kid);
1028 g_ptr_array_add(self->private->children, kid);
1031 /* and set the status based on the children */
1032 switch (nfailures) {
1034 self->private->status = RAIT_STATUS_COMPLETE;
1038 self->private->status = RAIT_STATUS_DEGRADED;
1042 self->private->status = RAIT_STATUS_FAILED;
1043 device_set_error(dself,
1044 _("more than one child device is missing"),
1045 DEVICE_STATUS_DEVICE_ERROR);
1049 /* create a name from the children's names and use it to chain up
1050 * to open_device (we skipped this step in rait_device_open_device) */
1051 device_name = child_device_names_to_rait_name(self);
1053 if (parent_class->open_device) {
1054 parent_class->open_device(dself,
1055 device_name, "rait",
1056 device_name+5); /* (+5 skips "rait:") */
1063 static void read_label_do_op(gpointer data,
1064 gpointer user_data G_GNUC_UNUSED) {
1065 GenericOp * op = data;
1066 op->result = GINT_TO_POINTER(device_read_label(op->child));
1069 static DeviceStatusFlags rait_device_read_label(Device * dself) {
1072 DeviceStatusFlags failed_result = 0;
1073 char *failed_errmsg = NULL;
1075 Device * first_success = NULL;
1077 self = RAIT_DEVICE(dself);
1079 amfree(dself->volume_time);
1080 amfree(dself->volume_label);
1081 dumpfile_free(dself->volume_header);
1082 dself->volume_header = NULL;
1084 if (rait_device_in_error(self))
1085 return dself->status | DEVICE_STATUS_DEVICE_ERROR;
1087 /* nail down our block size, if we haven't already */
1088 if (!fix_block_size(self))
1091 ops = make_generic_boolean_op_array(self);
1093 do_rait_child_ops(self, read_label_do_op, ops);
1095 for (i = 0; i < ops->len; i ++) {
1096 GenericOp * op = g_ptr_array_index(ops, i);
1097 DeviceStatusFlags result = GPOINTER_TO_INT(op->result);
1098 if (op->result == DEVICE_STATUS_SUCCESS) {
1099 if (first_success == NULL) {
1100 /* This is the first successful device. */
1101 first_success = op->child;
1102 } else if (!compare_volume_results(first_success, op->child)) {
1103 /* Doesn't match. :-( */
1104 failed_errmsg = vstrallocf("Inconsistent volume labels/datestamps: "
1105 "Got %s/%s on %s against %s/%s on %s.",
1106 first_success->volume_label,
1107 first_success->volume_time,
1108 first_success->device_name,
1109 op->child->volume_label,
1110 op->child->volume_time,
1111 op->child->device_name);
1112 g_warning("%s", failed_errmsg);
1113 failed_result |= DEVICE_STATUS_VOLUME_ERROR;
1116 failed_result |= result;
1120 if (failed_result != DEVICE_STATUS_SUCCESS) {
1121 /* We had multiple failures or an inconsistency. */
1122 device_set_error(dself, failed_errmsg, failed_result);
1124 /* Everything peachy. */
1125 amfree(failed_errmsg);
1127 g_assert(first_success != NULL);
1128 if (first_success->volume_label != NULL) {
1129 dself->volume_label = g_strdup(first_success->volume_label);
1131 if (first_success->volume_time != NULL) {
1132 dself->volume_time = g_strdup(first_success->volume_time);
1134 if (first_success->volume_header != NULL) {
1135 dself->volume_header = dumpfile_copy(first_success->volume_header);
1137 dself->header_block_size = first_success->header_block_size;
1140 g_ptr_array_free_full(ops);
1142 return dself->status;
1147 DeviceAccessMode mode; /* IN */
1148 char * label; /* IN */
1149 char * timestamp; /* IN */
1153 static void start_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1155 StartOp * param = data;
1157 klass = DEVICE_GET_CLASS(param->base.child);
1159 param->base.result =
1160 GINT_TO_POINTER((klass->start)(param->base.child,
1161 param->mode, param->label,
1164 param->base.result = FALSE;
1169 rait_device_configure(Device * dself, gboolean use_global_config)
1171 RaitDevice *self = RAIT_DEVICE(dself);
1174 for (i = 0; i < self->private->children->len; i ++) {
1177 if ((signed)i == self->private->failed)
1180 child = g_ptr_array_index(self->private->children, i);
1181 /* unconditionally configure the child without the global
1183 if (!device_configure(child, FALSE))
1187 if (parent_class->configure) {
1188 return parent_class->configure(dself, use_global_config);
1195 rait_device_start (Device * dself, DeviceAccessMode mode, char * label,
1201 DeviceStatusFlags total_status;
1202 char *failure_errmsgs = NULL;
1203 char * label_from_device = NULL;
1205 self = RAIT_DEVICE(dself);
1207 if (rait_device_in_error(self)) return FALSE;
1209 /* No starting in degraded mode. */
1210 if (self->private->status != RAIT_STATUS_COMPLETE &&
1211 (mode == ACCESS_WRITE || mode == ACCESS_APPEND)) {
1212 device_set_error(dself,
1213 g_strdup_printf(_("RAIT device %s is read-only "
1214 "because it is in degraded mode.\n"),
1215 dself->device_name),
1216 DEVICE_STATUS_DEVICE_ERROR);
1220 /* nail down our block size, if we haven't already */
1221 if (!fix_block_size(self))
1224 dself->access_mode = mode;
1225 dself->in_file = FALSE;
1226 amfree(dself->volume_label);
1227 amfree(dself->volume_time);
1228 dumpfile_free(dself->volume_header);
1229 dself->volume_header = NULL;
1231 ops = g_ptr_array_sized_new(self->private->children->len);
1232 for (i = 0; i < self->private->children->len; i ++) {
1235 if ((signed)i == self->private->failed) {
1239 op = g_new(StartOp, 1);
1240 op->base.child = g_ptr_array_index(self->private->children, i);
1242 op->label = g_strdup(label);
1243 op->timestamp = g_strdup(timestamp);
1244 g_ptr_array_add(ops, op);
1247 do_rait_child_ops(self, start_do_op, ops);
1249 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1251 /* Check results of starting devices; this is mostly about the
1252 * VOLUME_UNLABELED flag. */
1254 for (i = 0; i < ops->len; i ++) {
1255 StartOp * op = g_ptr_array_index(ops, i);
1256 Device *child = op->base.child;
1258 total_status |= child->status;
1259 if (child->status != DEVICE_STATUS_SUCCESS) {
1260 /* record the error message and move on. */
1261 append_message(&failure_errmsgs,
1262 g_strdup_printf("%s: %s",
1264 device_error_or_status(child)));
1266 if (child->volume_label != NULL && child->volume_time != NULL) {
1267 if (label_from_device) {
1268 if (strcmp(child->volume_label, dself->volume_label) != 0 ||
1269 strcmp(child->volume_time, dself->volume_time) != 0) {
1270 /* Mismatch! (Two devices provided different labels) */
1271 char * this_message =
1272 g_strdup_printf("%s: Label (%s/%s) is different "
1273 "from label (%s/%s) found at "
1276 child->volume_label,
1278 dself->volume_label,
1281 append_message(&failure_errmsgs, this_message);
1282 total_status |= DEVICE_STATUS_DEVICE_ERROR;
1283 g_warning("RAIT device children have different labels or timestamps");
1286 /* First device with a volume. */
1287 dself->volume_label = g_strdup(child->volume_label);
1288 dself->volume_time = g_strdup(child->volume_time);
1289 dself->volume_header = dumpfile_copy(child->volume_header);
1290 label_from_device = g_strdup(child->device_name);
1293 /* Device problem, it says it succeeded but sets no label? */
1294 char * this_message =
1295 g_strdup_printf("%s: Says label read, but no volume "
1296 "label found.", child->device_name);
1297 g_warning("RAIT device child has NULL volume or label");
1298 append_message(&failure_errmsgs, this_message);
1299 total_status |= DEVICE_STATUS_DEVICE_ERROR;
1304 if (total_status == DEVICE_STATUS_SUCCESS) {
1305 StartOp * op = g_ptr_array_index(ops, 0);
1306 Device *child = op->base.child;
1307 dself->header_block_size = child->header_block_size;
1310 amfree(label_from_device);
1311 g_ptr_array_free_full(ops);
1313 dself->status = total_status;
1315 if (total_status != DEVICE_STATUS_SUCCESS || !success) {
1316 device_set_error(dself, failure_errmsgs, total_status);
1319 amfree(failure_errmsgs);
1325 dumpfile_t * info; /* IN */
1330 static void start_file_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1331 StartFileOp * op = data;
1332 op->base.result = GINT_TO_POINTER(device_start_file(op->base.child,
1334 op->fileno = op->base.child->file;
1335 if (op->fileno < 1) {
1336 op->base.result = FALSE;
1341 rait_device_start_file (Device * dself, dumpfile_t * info) {
1346 int actual_file = -1;
1348 self = RAIT_DEVICE(dself);
1350 if (rait_device_in_error(self)) return FALSE;
1351 if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1353 ops = g_ptr_array_sized_new(self->private->children->len);
1354 for (i = 0; i < self->private->children->len; i ++) {
1356 op = g_new(StartFileOp, 1);
1357 op->base.child = g_ptr_array_index(self->private->children, i);
1358 /* each child gets its own copy of the header, to munge as it
1359 * likes (setting blocksize, at least) */
1360 op->info = dumpfile_copy(info);
1361 g_ptr_array_add(ops, op);
1364 do_rait_child_ops(self, start_file_do_op, ops);
1366 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1368 for (i = 0; i < self->private->children->len && success; i ++) {
1369 StartFileOp * op = g_ptr_array_index(ops, i);
1370 if (!op->base.result)
1372 g_assert(op->fileno >= 1);
1373 if (actual_file < 1) {
1374 actual_file = op->fileno;
1376 if (actual_file != op->fileno) {
1377 /* File number mismatch! Aah, my hair is on fire! */
1378 device_set_error(dself,
1379 g_strdup_printf("File number mismatch in "
1380 "rait_device_start_file(): "
1381 "Child %s reported file number "
1382 "%d, another child reported "
1384 op->base.child->device_name,
1385 op->fileno, actual_file),
1386 DEVICE_STATUS_DEVICE_ERROR);
1388 op->base.result = FALSE;
1392 for (i = 0; i < ops->len && success; i ++) {
1393 StartFileOp * op = g_ptr_array_index(ops, i);
1394 if (op->info) dumpfile_free(op->info);
1396 g_ptr_array_free_full(ops);
1399 if (!device_in_error(dself)) {
1400 device_set_error(dself, stralloc("One or more devices "
1401 "failed to start_file"),
1402 DEVICE_STATUS_DEVICE_ERROR);
1407 dself->in_file = TRUE;
1408 g_assert(actual_file >= 1);
1409 dself->file = actual_file;
1414 static void find_simple_params(RaitDevice * self,
1415 guint * num_children,
1416 guint * data_children) {
1419 num = self->private->children->len;
1424 if (num_children != NULL)
1425 *num_children = num;
1426 if (data_children != NULL)
1427 *data_children = data;
1432 guint size; /* IN */
1433 gpointer data; /* IN */
1434 gboolean data_needs_free; /* bookkeeping */
1438 static void write_block_do_op(gpointer data,
1439 gpointer user_data G_GNUC_UNUSED) {
1440 WriteBlockOp * op = data;
1443 GINT_TO_POINTER(device_write_block(op->base.child, op->size, op->data));
1446 /* Parity block generation. Performance of this function can be improved
1447 considerably by using larger-sized integers or
1448 assembly-coded vector instructions. Parameters are:
1449 % data - All data chunks in series (chunk_size * num_chunks bytes)
1450 % parity - Allocated space for parity block (chunk_size bytes)
1452 static void make_parity_block(char * data, char * parity,
1453 guint chunk_size, guint num_chunks) {
1455 bzero(parity, chunk_size);
1456 for (i = 0; i < num_chunks - 1; i ++) {
1458 for (j = 0; j < chunk_size; j ++) {
1459 parity[j] ^= data[chunk_size*i + j];
1464 /* Does the same thing as make_parity_block, but instead of using a
1465 single memory chunk holding all chunks, it takes a GPtrArray of
1467 static void make_parity_block_extents(GPtrArray * data, char * parity,
1470 bzero(parity, chunk_size);
1471 for (i = 0; i < data->len; i ++) {
1474 data_chunk = g_ptr_array_index(data, i);
1475 for (j = 0; j < chunk_size; j ++) {
1476 parity[j] ^= data_chunk[j];
1481 /* Does the parity creation algorithm. Allocates and returns a single
1482 device block from a larger RAIT block. chunks and chunk are 1-indexed. */
1483 static char * extract_data_block(char * data, guint size,
1484 guint chunks, guint chunk) {
1488 g_assert(chunks > 0 && chunk > 0 && chunk <= chunks);
1489 g_assert(data != NULL);
1490 g_assert(size > 0 && size % (chunks - 1) == 0);
1492 chunk_size = size / (chunks - 1);
1493 rval = g_malloc(chunk_size);
1494 if (chunks != chunk) {
1496 memcpy(rval, data + chunk_size * (chunk - 1), chunk_size);
1498 make_parity_block(data, rval, chunk_size, chunks);
1505 rait_device_write_block (Device * dself, guint size, gpointer data) {
1509 guint data_children, num_children;
1510 gsize blocksize = dself->block_size;
1512 gboolean last_block = (size < blocksize);
1514 self = RAIT_DEVICE(dself);
1516 if (rait_device_in_error(self)) return FALSE;
1517 if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1519 find_simple_params(RAIT_DEVICE(self), &num_children, &data_children);
1520 num_children = self->private->children->len;
1521 if (num_children != 1)
1522 data_children = num_children - 1;
1524 data_children = num_children;
1526 g_assert(size % data_children == 0 || last_block);
1528 /* zero out to the end of a short block -- tape devices only write
1533 new_data = g_malloc(blocksize);
1534 memcpy(new_data, data, size);
1535 bzero(new_data + size, blocksize - size);
1541 ops = g_ptr_array_sized_new(num_children);
1542 for (i = 0; i < self->private->children->len; i ++) {
1544 op = g_malloc(sizeof(*op));
1545 op->base.child = g_ptr_array_index(self->private->children, i);
1546 op->size = size / data_children;
1547 if (num_children <= 2) {
1549 op->data_needs_free = FALSE;
1551 op->data_needs_free = TRUE;
1552 op->data = extract_data_block(data, size, num_children, i + 1);
1554 g_ptr_array_add(ops, op);
1557 do_rait_child_ops(self, write_block_do_op, ops);
1559 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1561 for (i = 0; i < self->private->children->len; i ++) {
1562 WriteBlockOp * op = g_ptr_array_index(ops, i);
1563 if (op->data_needs_free)
1571 g_ptr_array_free_full(ops);
1574 /* TODO be more specific here */
1575 /* TODO: handle EOM here -- if one or more (or two or more??)
1576 * children have is_eom set, then reflect that in our error
1577 * status. What's more fun is when one device fails and must be isolated at
1578 * the same time another hits EOF. */
1579 device_set_error(dself,
1580 stralloc("One or more devices failed to write_block"),
1581 DEVICE_STATUS_DEVICE_ERROR);
1582 /* this is EOM or an error, so call it EOM */
1583 dself->is_eom = TRUE;
1593 static void finish_file_do_op(gpointer data,
1594 gpointer user_data G_GNUC_UNUSED) {
1595 GenericOp * op = data;
1597 op->result = GINT_TO_POINTER(device_finish_file(op->child));
1604 rait_device_finish_file (Device * dself) {
1607 RaitDevice * self = RAIT_DEVICE(dself);
1609 g_assert(self != NULL);
1610 if (rait_device_in_error(dself)) return FALSE;
1611 if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1613 ops = make_generic_boolean_op_array(self);
1615 do_rait_child_ops(self, finish_file_do_op, ops);
1617 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1619 g_ptr_array_free_full(ops);
1622 /* TODO: be more specific here */
1623 device_set_error(dself,
1624 g_strdup("One or more devices failed to finish_file"),
1625 DEVICE_STATUS_DEVICE_ERROR);
1629 dself->in_file = FALSE;
1635 guint requested_file; /* IN */
1636 guint actual_file; /* OUT */
1640 static void seek_file_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1641 SeekFileOp * op = data;
1642 op->base.result = device_seek_file(op->base.child, op->requested_file);
1643 op->actual_file = op->base.child->file;
1647 rait_device_seek_file (Device * dself, guint file) {
1652 RaitDevice * self = RAIT_DEVICE(dself);
1653 guint actual_file = 0;
1654 gboolean in_file = FALSE;
1656 if (rait_device_in_error(self)) return NULL;
1658 dself->in_file = FALSE;
1659 dself->is_eof = FALSE;
1662 ops = g_ptr_array_sized_new(self->private->children->len);
1663 for (i = 0; i < self->private->children->len; i ++) {
1665 if ((int)i == self->private->failed)
1666 continue; /* This device is broken. */
1667 op = g_new(SeekFileOp, 1);
1668 op->base.child = g_ptr_array_index(self->private->children, i);
1669 op->base.child_index = i;
1670 op->requested_file = file;
1671 g_ptr_array_add(ops, op);
1674 do_rait_child_ops(self, seek_file_do_op, ops);
1676 /* This checks for NULL values, but we still have to check for
1677 consistant headers. */
1678 success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1679 ops, extract_boolean_pointer_op);
1682 for (i = 0; i < ops->len; i ++) {
1683 SeekFileOp * this_op;
1684 dumpfile_t * this_result;
1685 guint this_actual_file;
1686 gboolean this_in_file;
1688 this_op = (SeekFileOp*)g_ptr_array_index(ops, i);
1690 if ((signed)this_op->base.child_index == self->private->failed)
1693 this_result = this_op->base.result;
1694 this_actual_file = this_op->actual_file;
1695 this_in_file = this_op->base.child->in_file;
1699 actual_file = this_actual_file;
1700 in_file = this_in_file;
1702 if (headers_are_equal(rval, this_result) &&
1703 actual_file == this_actual_file &&
1704 in_file == this_in_file) {
1713 g_ptr_array_free_full(ops);
1717 /* TODO: be more specific here */
1718 device_set_error(dself,
1719 g_strdup("One or more devices failed to seek_file"),
1720 DEVICE_STATUS_DEVICE_ERROR);
1724 /* update our state */
1725 dself->in_file = in_file;
1726 dself->file = actual_file;
1733 guint64 block; /* IN */
1737 static void seek_block_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1738 SeekBlockOp * op = data;
1740 GINT_TO_POINTER(device_seek_block(op->base.child, op->block));
1744 rait_device_seek_block (Device * dself, guint64 block) {
1749 RaitDevice * self = RAIT_DEVICE(dself);
1751 if (rait_device_in_error(self)) return FALSE;
1753 ops = g_ptr_array_sized_new(self->private->children->len);
1754 for (i = 0; i < self->private->children->len; i ++) {
1756 if ((int)i == self->private->failed)
1757 continue; /* This device is broken. */
1758 op = g_new(SeekBlockOp, 1);
1759 op->base.child = g_ptr_array_index(self->private->children, i);
1760 op->base.child_index = i;
1762 g_ptr_array_add(ops, op);
1765 do_rait_child_ops(self, seek_block_do_op, ops);
1767 success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1768 ops, extract_boolean_generic_op);
1770 g_ptr_array_free_full(ops);
1773 /* TODO: be more specific here */
1774 device_set_error(dself,
1775 stralloc("One or more devices failed to seek_block"),
1776 DEVICE_STATUS_DEVICE_ERROR);
1780 dself->block = block;
1786 gpointer buffer; /* IN */
1787 int read_size; /* IN/OUT -- note not a pointer */
1788 int desired_read_size; /* bookkeeping */
1792 static void read_block_do_op(gpointer data,
1793 gpointer user_data G_GNUC_UNUSED) {
1794 ReadBlockOp * op = data;
1796 GINT_TO_POINTER(device_read_block(op->base.child, op->buffer,
1798 if (op->read_size > op->desired_read_size) {
1799 g_warning("child device %s tried to return an oversized block, which the RAIT device does not support",
1800 op->base.child->device_name);
1804 /* A BooleanExtractor. This one checks for a successful read. */
1805 static gboolean extract_boolean_read_block_op_data(gpointer data) {
1806 ReadBlockOp * op = data;
1807 return GPOINTER_TO_INT(op->base.result) == op->desired_read_size;
1810 /* A BooleanExtractor. This one checks for EOF. */
1811 static gboolean extract_boolean_read_block_op_eof(gpointer data) {
1812 ReadBlockOp * op = data;
1813 return op->base.child->is_eof;
1816 /* Counts the number of elements in an array matching a given proposition. */
1817 static int g_ptr_array_count(GPtrArray * array, BooleanExtractor filter) {
1821 for (i = 0; i < array->len ; i++) {
1822 if (filter(g_ptr_array_index(array, i)))
1828 static gboolean raid_block_reconstruction(RaitDevice * self, GPtrArray * ops,
1829 gpointer buf, size_t bufsize) {
1830 guint num_children, data_children;
1832 gsize child_blocksize;
1835 gpointer parity_block = NULL;
1840 blocksize = DEVICE(self)->block_size;
1841 find_simple_params(self, &num_children, &data_children);
1843 if (num_children > 1)
1844 parity_child = num_children - 1;
1848 child_blocksize = blocksize / data_children;
1850 for (i = 0; i < ops->len; i ++) {
1851 ReadBlockOp * op = g_ptr_array_index(ops, i);
1852 if (!extract_boolean_read_block_op_data(op))
1854 if ((int)(op->base.child_index) == parity_child) {
1855 parity_block = op->buffer;
1857 g_assert(child_blocksize * (op->base.child_index+1) <= bufsize);
1858 memcpy((char *)buf + child_blocksize * op->base.child_index, op->buffer,
1862 if (self->private->status == RAIT_STATUS_COMPLETE) {
1863 g_assert(parity_block != NULL); /* should have found parity_child */
1865 if (num_children >= 2) {
1866 /* Verify the parity block. This code is inefficient but
1867 does the job for the 2-device case, too. */
1868 gpointer constructed_parity;
1869 GPtrArray * data_extents;
1871 constructed_parity = g_malloc(child_blocksize);
1872 data_extents = g_ptr_array_sized_new(data_children);
1873 for (i = 0; i < data_children; i ++) {
1874 ReadBlockOp * op = g_ptr_array_index(ops, i);
1875 g_assert(extract_boolean_read_block_op_data(op));
1876 if ((int)op->base.child_index == parity_child)
1878 g_ptr_array_add(data_extents, op->buffer);
1880 make_parity_block_extents(data_extents, constructed_parity,
1883 if (0 != memcmp(parity_block, constructed_parity,
1885 device_set_error(DEVICE(self),
1886 stralloc(_("RAIT is inconsistent: Parity block did not match data blocks.")),
1887 DEVICE_STATUS_DEVICE_ERROR);
1888 /* TODO: can't we just isolate the device in this case? */
1891 g_ptr_array_free(data_extents, TRUE);
1892 amfree(constructed_parity);
1893 } else { /* do nothing. */ }
1894 } else if (self->private->status == RAIT_STATUS_DEGRADED) {
1895 g_assert(self->private->failed >= 0 && self->private->failed < (int)num_children);
1896 /* We are in degraded mode. What's missing? */
1897 if (self->private->failed == parity_child) {
1899 } else if (num_children >= 2) {
1900 /* Reconstruct failed block from parity block. */
1901 GPtrArray * data_extents = g_ptr_array_new();
1903 for (i = 0; i < data_children; i ++) {
1904 ReadBlockOp * op = g_ptr_array_index(ops, i);
1905 if (!extract_boolean_read_block_op_data(op))
1907 g_ptr_array_add(data_extents, op->buffer);
1910 /* Conveniently, the reconstruction is the same procedure
1911 as the parity generation. This even works if there is
1912 only one remaining device! */
1913 make_parity_block_extents(data_extents,
1914 (char *)buf + (child_blocksize *
1915 self->private->failed),
1918 /* The array members belong to our ops argument. */
1919 g_ptr_array_free(data_extents, TRUE);
1921 g_assert_not_reached();
1924 /* device is already in FAILED state -- we shouldn't even be here */
1931 rait_device_read_block (Device * dself, gpointer buf, int * size) {
1935 guint num_children, data_children;
1936 gsize blocksize = dself->block_size;
1937 gsize child_blocksize;
1939 RaitDevice * self = RAIT_DEVICE(dself);
1941 if (rait_device_in_error(self)) return -1;
1943 find_simple_params(self, &num_children, &data_children);
1945 /* tell caller they haven't given us a big enough buffer */
1946 if (blocksize > (gsize)*size) {
1947 g_assert(blocksize < INT_MAX);
1948 *size = (int)blocksize;
1952 g_assert(blocksize % data_children == 0); /* see find_block_size */
1953 child_blocksize = blocksize / data_children;
1955 ops = g_ptr_array_sized_new(num_children);
1956 for (i = 0; i < num_children; i ++) {
1958 if ((int)i == self->private->failed)
1959 continue; /* This device is broken. */
1960 op = g_new(ReadBlockOp, 1);
1961 op->base.child = g_ptr_array_index(self->private->children, i);
1962 op->base.child_index = i;
1963 op->buffer = g_malloc(child_blocksize);
1964 op->desired_read_size = op->read_size = child_blocksize;
1965 g_ptr_array_add(ops, op);
1968 do_rait_child_ops(self, read_block_do_op, ops);
1970 if (g_ptr_array_count(ops, extract_boolean_read_block_op_data)) {
1971 if (!g_ptr_array_union_robust(RAIT_DEVICE(self),
1973 extract_boolean_read_block_op_data)) {
1974 /* TODO: be more specific */
1975 device_set_error(dself,
1976 stralloc(_("Error occurred combining blocks from child devices")),
1977 DEVICE_STATUS_DEVICE_ERROR);
1980 /* raid_block_reconstruction sets the error status if necessary */
1981 success = raid_block_reconstruction(RAIT_DEVICE(self),
1982 ops, buf, (size_t)*size);
1986 if (g_ptr_array_union_robust(RAIT_DEVICE(self),
1988 extract_boolean_read_block_op_eof)) {
1989 device_set_error(dself,
1991 DEVICE_STATUS_SUCCESS);
1992 dself->is_eof = TRUE;
1993 dself->in_file = FALSE;
1995 device_set_error(dself,
1996 stralloc(_("All child devices failed to read, but not all are at eof")),
1997 DEVICE_STATUS_DEVICE_ERROR);
2001 for (i = 0; i < ops->len; i ++) {
2002 ReadBlockOp * op = g_ptr_array_index(ops, i);
2005 g_ptr_array_free_full(ops);
2016 /* property utility functions */
2020 DevicePropertyId id; /* IN */
2021 GValue value; /* IN/OUT */
2022 PropertySurety surety; /* IN (for set) */
2023 PropertySource source; /* IN (for set) */
2026 /* Creates a GPtrArray of PropertyOf for a get or set operation. */
2027 static GPtrArray * make_property_op_array(RaitDevice * self,
2028 DevicePropertyId id,
2030 PropertySurety surety,
2031 PropertySource source) {
2034 ops = g_ptr_array_sized_new(self->private->children->len);
2035 for (i = 0; i < self->private->children->len; i ++) {
2038 if ((signed)i == self->private->failed) {
2042 op = g_new(PropertyOp, 1);
2043 op->base.child = g_ptr_array_index(self->private->children, i);
2045 bzero(&(op->value), sizeof(op->value));
2046 if (value != NULL) {
2047 g_value_unset_copy(value, &(op->value));
2049 op->surety = surety;
2050 op->source = source;
2051 g_ptr_array_add(ops, op);
2058 static void property_get_do_op(gpointer data,
2059 gpointer user_data G_GNUC_UNUSED) {
2060 PropertyOp * op = data;
2062 bzero(&(op->value), sizeof(op->value));
2064 GINT_TO_POINTER(device_property_get(op->base.child, op->id,
2069 static void property_set_do_op(gpointer data,
2070 gpointer user_data G_GNUC_UNUSED) {
2071 PropertyOp * op = data;
2074 GINT_TO_POINTER(device_property_set_ex(op->base.child, op->id,
2075 &(op->value), op->surety,
2077 g_value_unset(&(op->value));
2080 /* PropertyGetFns and PropertySetFns */
2083 property_get_block_size_fn(Device *dself,
2084 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2085 PropertySurety *surety, PropertySource *source)
2087 RaitDevice *self = RAIT_DEVICE(dself);
2088 gsize my_block_size;
2090 if (dself->block_size_source != PROPERTY_SOURCE_DEFAULT) {
2091 my_block_size = dself->block_size;
2094 *surety = dself->block_size_surety;
2096 gsize child_block_size;
2097 child_block_size = calculate_block_size_from_children(self,
2099 if (child_block_size == 0)
2103 *surety = PROPERTY_SURETY_BAD; /* may still change */
2107 g_value_unset_init(val, G_TYPE_INT);
2108 g_assert(my_block_size < G_MAXINT); /* gsize -> gint */
2109 g_value_set_int(val, (gint)my_block_size);
2113 *source = dself->block_size_source;
2119 property_set_block_size_fn(Device *dself,
2120 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2121 PropertySurety surety, PropertySource source)
2123 RaitDevice *self = RAIT_DEVICE(dself);
2124 gint my_block_size = g_value_get_int(val);
2125 guint data_children;
2127 find_simple_params(self, NULL, &data_children);
2128 if ((my_block_size % data_children) != 0) {
2129 device_set_error(dself,
2130 vstrallocf(_("Block size must be a multiple of %d"), data_children),
2131 DEVICE_STATUS_DEVICE_ERROR);
2135 dself->block_size = my_block_size;
2136 dself->block_size_source = source;
2137 dself->block_size_surety = surety;
2139 if (!fix_block_size(self))
2146 property_get_canonical_name_fn(Device *dself,
2147 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2148 PropertySurety *surety, PropertySource *source)
2150 RaitDevice *self = RAIT_DEVICE(dself);
2151 char *canonical = child_device_names_to_rait_name(self);
2154 g_value_unset_init(val, G_TYPE_STRING);
2155 g_value_set_string(val, canonical);
2160 *surety = PROPERTY_SURETY_GOOD;
2163 *source = PROPERTY_SOURCE_DETECTED;
2169 property_get_concurrency_fn(Device *dself,
2170 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2171 PropertySurety *surety, PropertySource *source)
2173 RaitDevice *self = RAIT_DEVICE(dself);
2174 ConcurrencyParadigm result;
2179 ops = make_property_op_array(self, PROPERTY_CONCURRENCY, NULL, 0, 0);
2180 do_rait_child_ops(self, property_get_do_op, ops);
2182 /* find the most restrictive paradigm acceptable to all
2184 result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
2186 for (i = 0; i < ops->len; i ++) {
2187 ConcurrencyParadigm cur;
2188 PropertyOp * op = g_ptr_array_index(ops, i);
2190 if (!op->base.result
2191 || G_VALUE_TYPE(&(op->value)) != CONCURRENCY_PARADIGM_TYPE) {
2196 cur = g_value_get_enum(&(op->value));
2197 if (result == CONCURRENCY_PARADIGM_EXCLUSIVE ||
2198 cur == CONCURRENCY_PARADIGM_EXCLUSIVE) {
2199 result = CONCURRENCY_PARADIGM_EXCLUSIVE;
2200 } else if (result == CONCURRENCY_PARADIGM_SHARED_READ ||
2201 cur == CONCURRENCY_PARADIGM_SHARED_READ) {
2202 result = CONCURRENCY_PARADIGM_SHARED_READ;
2203 } else if (result == CONCURRENCY_PARADIGM_RANDOM_ACCESS &&
2204 cur == CONCURRENCY_PARADIGM_RANDOM_ACCESS) {
2205 result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
2212 g_ptr_array_free_full(ops);
2216 g_value_unset_init(val, CONCURRENCY_PARADIGM_TYPE);
2217 g_value_set_enum(val, result);
2221 *surety = PROPERTY_SURETY_GOOD;
2224 *source = PROPERTY_SOURCE_DETECTED;
2231 property_get_streaming_fn(Device *dself,
2232 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2233 PropertySurety *surety, PropertySource *source)
2235 RaitDevice *self = RAIT_DEVICE(dself);
2236 StreamingRequirement result;
2241 ops = make_property_op_array(self, PROPERTY_STREAMING, NULL, 0, 0);
2242 do_rait_child_ops(self, property_get_do_op, ops);
2244 /* combine the child streaming requirements, selecting the strongest
2245 * requirement of the bunch. */
2246 result = STREAMING_REQUIREMENT_NONE;
2248 for (i = 0; i < ops->len; i ++) {
2249 StreamingRequirement cur;
2250 PropertyOp * op = g_ptr_array_index(ops, i);
2252 if (!op->base.result
2253 || G_VALUE_TYPE(&(op->value)) != STREAMING_REQUIREMENT_TYPE) {
2258 cur = g_value_get_enum(&(op->value));
2259 if (result == STREAMING_REQUIREMENT_REQUIRED ||
2260 cur == STREAMING_REQUIREMENT_REQUIRED) {
2261 result = STREAMING_REQUIREMENT_REQUIRED;
2262 } else if (result == STREAMING_REQUIREMENT_DESIRED ||
2263 cur == STREAMING_REQUIREMENT_DESIRED) {
2264 result = STREAMING_REQUIREMENT_DESIRED;
2265 } else if (result == STREAMING_REQUIREMENT_NONE &&
2266 cur == STREAMING_REQUIREMENT_NONE) {
2267 result = STREAMING_REQUIREMENT_NONE;
2274 g_ptr_array_free_full(ops);
2278 g_value_unset_init(val, STREAMING_REQUIREMENT_TYPE);
2279 g_value_set_enum(val, result);
2283 *surety = PROPERTY_SURETY_GOOD;
2286 *source = PROPERTY_SOURCE_DETECTED;
2293 property_get_boolean_and_fn(Device *dself,
2294 DevicePropertyBase *base, GValue *val,
2295 PropertySurety *surety, PropertySource *source)
2297 RaitDevice *self = RAIT_DEVICE(dself);
2303 ops = make_property_op_array(self, base->ID, NULL, 0, 0);
2304 do_rait_child_ops(self, property_get_do_op, ops);
2306 /* combine the child values, applying a simple AND */
2309 for (i = 0; i < ops->len; i ++) {
2310 PropertyOp * op = g_ptr_array_index(ops, i);
2312 if (!op->base.result || !G_VALUE_HOLDS_BOOLEAN(&(op->value))) {
2317 if (!g_value_get_boolean(&(op->value))) {
2323 g_ptr_array_free_full(ops);
2327 g_value_unset_init(val, G_TYPE_BOOLEAN);
2328 g_value_set_boolean(val, result);
2332 *surety = PROPERTY_SURETY_GOOD;
2335 *source = PROPERTY_SOURCE_DETECTED;
2342 property_get_medium_access_type_fn(Device *dself,
2343 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2344 PropertySurety *surety, PropertySource *source)
2346 RaitDevice *self = RAIT_DEVICE(dself);
2347 MediaAccessMode result;
2352 ops = make_property_op_array(self, PROPERTY_MEDIUM_ACCESS_TYPE, NULL, 0, 0);
2353 do_rait_child_ops(self, property_get_do_op, ops);
2355 /* combine the modes as best we can */
2358 for (i = 0; i < ops->len; i ++) {
2359 MediaAccessMode cur;
2360 PropertyOp * op = g_ptr_array_index(ops, i);
2362 if (!op->base.result || G_VALUE_TYPE(&(op->value)) != MEDIA_ACCESS_MODE_TYPE) {
2367 cur = g_value_get_enum(&(op->value));
2371 } else if ((result == MEDIA_ACCESS_MODE_READ_ONLY &&
2372 cur == MEDIA_ACCESS_MODE_WRITE_ONLY) ||
2373 (result == MEDIA_ACCESS_MODE_WRITE_ONLY &&
2374 cur == MEDIA_ACCESS_MODE_READ_ONLY)) {
2375 /* Invalid combination; one device can only read, other
2379 } else if (result == MEDIA_ACCESS_MODE_READ_ONLY ||
2380 cur == MEDIA_ACCESS_MODE_READ_ONLY) {
2381 result = MEDIA_ACCESS_MODE_READ_ONLY;
2382 } else if (result == MEDIA_ACCESS_MODE_WRITE_ONLY ||
2383 cur == MEDIA_ACCESS_MODE_WRITE_ONLY) {
2384 result = MEDIA_ACCESS_MODE_WRITE_ONLY;
2385 } else if (result == MEDIA_ACCESS_MODE_WORM ||
2386 cur == MEDIA_ACCESS_MODE_WORM) {
2387 result = MEDIA_ACCESS_MODE_WORM;
2388 } else if (result == MEDIA_ACCESS_MODE_READ_WRITE &&
2389 cur == MEDIA_ACCESS_MODE_READ_WRITE) {
2390 result = MEDIA_ACCESS_MODE_READ_WRITE;
2397 g_ptr_array_free_full(ops);
2401 g_value_unset_init(val, MEDIA_ACCESS_MODE_TYPE);
2402 g_value_set_enum(val, result);
2406 *surety = PROPERTY_SURETY_GOOD;
2409 *source = PROPERTY_SOURCE_DETECTED;
2416 property_get_max_volume_usage_fn(Device *dself,
2417 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2418 PropertySurety *surety, PropertySource *source)
2420 RaitDevice *self = RAIT_DEVICE(dself);
2424 guint data_children;
2426 ops = make_property_op_array(self, PROPERTY_MAX_VOLUME_USAGE, NULL, 0, 0);
2427 do_rait_child_ops(self, property_get_do_op, ops);
2429 /* look for the smallest value that is set */
2431 for (i = 0; i < ops->len; i ++) {
2433 PropertyOp * op = g_ptr_array_index(ops, i);
2435 if (!op->base.result || !G_VALUE_HOLDS_UINT64(&(op->value))) {
2436 continue; /* ignore children without this property */
2439 cur = g_value_get_uint64(&(op->value));
2441 if (!result || (cur && cur < result)) {
2446 g_ptr_array_free_full(ops);
2449 /* result contains the minimum usage on any child. We can use that space
2450 * on each of our data children, so the total is larger */
2451 find_simple_params(self, NULL, &data_children);
2452 result *= data_children;
2455 g_value_unset_init(val, G_TYPE_UINT64);
2456 g_value_set_uint64(val, result);
2460 *surety = PROPERTY_SURETY_GOOD;
2463 *source = PROPERTY_SOURCE_DETECTED;
2467 /* no result from any children, so we effectively don't have this property */
2473 property_set_max_volume_usage_fn(Device *dself,
2474 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2475 PropertySurety surety, PropertySource source)
2477 RaitDevice *self = RAIT_DEVICE(dself);
2478 guint64 parent_usage;
2479 guint64 child_usage;
2484 guint data_children;
2486 parent_usage = g_value_get_uint64(val);
2487 find_simple_params(self, NULL, &data_children);
2489 child_usage = parent_usage / data_children;
2491 bzero(&child_val, sizeof(child_val));
2492 g_value_init(&child_val, G_TYPE_UINT64);
2493 g_value_set_uint64(&child_val, child_usage);
2495 ops = make_property_op_array(self, PROPERTY_MAX_VOLUME_USAGE,
2496 &child_val, surety, source);
2497 do_rait_child_ops(self, property_set_do_op, ops);
2499 /* if any of the kids succeeded, then we did too */
2501 for (i = 0; i < ops->len; i ++) {
2502 PropertyOp * op = g_ptr_array_index(ops, i);
2504 if (op->base.result) {
2510 g_ptr_array_free_full(ops);
2521 static void recycle_file_do_op(gpointer data,
2522 gpointer user_data G_GNUC_UNUSED) {
2523 RecycleFileOp * op = data;
2525 GINT_TO_POINTER(device_recycle_file(op->base.child, op->filenum));
2529 rait_device_recycle_file (Device * dself, guint filenum) {
2534 RaitDevice * self = RAIT_DEVICE(dself);
2536 if (rait_device_in_error(self)) return FALSE;
2538 ops = g_ptr_array_sized_new(self->private->children->len);
2539 for (i = 0; i < self->private->children->len; i ++) {
2541 op = g_new(RecycleFileOp, 1);
2542 op->base.child = g_ptr_array_index(self->private->children, i);
2543 op->filenum = filenum;
2544 g_ptr_array_add(ops, op);
2547 do_rait_child_ops(self, recycle_file_do_op, ops);
2549 success = g_ptr_array_and(ops, extract_boolean_generic_op);
2551 g_ptr_array_free_full(ops);
2554 /* TODO: be more specific here */
2555 device_set_error(dself,
2556 stralloc(_("One or more devices failed to recycle_file")),
2557 DEVICE_STATUS_DEVICE_ERROR);
2564 static void finish_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
2565 GenericOp * op = data;
2566 op->result = GINT_TO_POINTER(device_finish(op->child));
2570 rait_device_finish (Device * self) {
2573 gboolean rval = TRUE;
2575 rval = !rait_device_in_error(self);
2577 ops = make_generic_boolean_op_array(RAIT_DEVICE(self));
2579 do_rait_child_ops(RAIT_DEVICE(self), finish_do_op, ops);
2581 success = g_ptr_array_and(ops, extract_boolean_generic_op);
2585 g_ptr_array_free_full(ops);
2587 self->access_mode = ACCESS_NULL;
2593 rait_device_factory (char * device_name, char * device_type, char * device_node) {
2595 g_assert(0 == strcmp(device_type, "rait"));
2596 rval = DEVICE(g_object_new(TYPE_RAIT_DEVICE, NULL));
2597 device_open_device(rval, device_name, device_type, device_node);
2602 rait_device_register (void) {
2603 static const char * device_prefix_list[] = {"rait", NULL};
2604 register_device(rait_device_factory, device_prefix_list);