2 * Copyright (c) 2005 Zmanda, Inc. All Rights Reserved.
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 2.1 as
6 * published by the Free Software Foundation.
8 * This library 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 Lesser General Public
11 * License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17 * Contact information: Zmanda Inc., 505 N Mathlida Ave, Suite 120
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. */
24 #include "rait-device.h"
30 RAIT_STATUS_COMPLETE, /* All subdevices OK. */
31 RAIT_STATUS_DEGRADED, /* One subdevice failed. */
32 RAIT_STATUS_FAILED /* Two or more subdevices failed. */
35 struct RaitDevicePrivate_s {
37 /* These flags are only relevant for reading. */
39 /* If status == RAIT_STATUS_DEGRADED, this holds the index of the
40 failed node. It holds a negative number otherwise. */
45 #define PRIVATE(o) (o->private)
47 /* here are local prototypes */
48 static void rait_device_init (RaitDevice * o);
49 static void rait_device_class_init (RaitDeviceClass * c);
50 static gboolean rait_device_open_device (Device * self, char * device_name);
51 static gboolean rait_device_start (Device * self, DeviceAccessMode mode,
52 char * label, char * timestamp);
53 static gboolean rait_device_start_file(Device * self, const dumpfile_t * info);
54 static gboolean rait_device_write_block (Device * self, guint size,
55 gpointer data, gboolean last_block);
56 static gboolean rait_device_finish_file (Device * self);
57 static dumpfile_t * rait_device_seek_file (Device * self, guint file);
58 static gboolean rait_device_seek_block (Device * self, guint64 block);
59 static int rait_device_read_block (Device * self, gpointer buf,
61 static gboolean rait_device_property_get (Device * self, DevicePropertyId id,
63 static gboolean rait_device_property_set (Device * self, DevicePropertyId id,
65 static gboolean rait_device_recycle_file (Device * self, guint filenum);
66 static gboolean rait_device_finish (Device * self);
67 static ReadLabelStatusFlags rait_device_read_label(Device * dself);
68 static void find_simple_params(RaitDevice * self, guint * num_children,
69 guint * data_children, int * blocksize);
71 /* pointer to the class of our parent */
72 static DeviceClass *parent_class = NULL;
74 /* This function is replicated here in case we have GLib from before 2.4.
75 * It should probably go eventually. */
76 #if !GLIB_CHECK_VERSION(2,4,0)
78 g_ptr_array_foreach (GPtrArray *array,
84 g_return_if_fail (array);
86 for (i = 0; i < array->len; i++)
87 (*func) (array->pdata[i], user_data);
92 rait_device_get_type (void)
94 static GType type = 0;
96 if G_UNLIKELY(type == 0) {
97 static const GTypeInfo info = {
98 sizeof (RaitDeviceClass),
100 (GBaseFinalizeFunc) NULL,
101 (GClassInitFunc) rait_device_class_init,
102 (GClassFinalizeFunc) NULL,
103 NULL /* class_data */,
106 (GInstanceInitFunc) rait_device_init,
110 type = g_type_register_static (TYPE_DEVICE, "RaitDevice", &info,
117 static void g_object_unref_foreach(gpointer data,
118 gpointer user_data G_GNUC_UNUSED) {
119 g_return_if_fail(G_IS_OBJECT(data));
120 g_object_unref(data);
124 rait_device_finalize(GObject *obj_self)
126 RaitDevice *self G_GNUC_UNUSED = RAIT_DEVICE (obj_self);
127 if(G_OBJECT_CLASS(parent_class)->finalize) \
128 (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
129 if(self->private->children) {
130 g_ptr_array_foreach(self->private->children,
131 g_object_unref_foreach, NULL);
132 g_ptr_array_free (self->private->children, TRUE);
133 self->private->children = NULL;
135 amfree(self->private);
139 rait_device_init (RaitDevice * o G_GNUC_UNUSED)
141 PRIVATE(o) = malloc(sizeof(RaitDevicePrivate));
142 PRIVATE(o)->children = g_ptr_array_new();
143 PRIVATE(o)->status = RAIT_STATUS_COMPLETE;
144 PRIVATE(o)->failed = -1;
148 rait_device_class_init (RaitDeviceClass * c G_GNUC_UNUSED)
150 GObjectClass *g_object_class G_GNUC_UNUSED = (GObjectClass*) c;
151 DeviceClass *device_class = (DeviceClass *)c;
153 parent_class = g_type_class_ref (TYPE_DEVICE);
155 device_class->open_device = rait_device_open_device;
156 device_class->start = rait_device_start;
157 device_class->start_file = rait_device_start_file;
158 device_class->write_block = rait_device_write_block;
159 device_class->finish_file = rait_device_finish_file;
160 device_class->seek_file = rait_device_seek_file;
161 device_class->seek_block = rait_device_seek_block;
162 device_class->read_block = rait_device_read_block;
163 device_class->property_get = rait_device_property_get;
164 device_class->property_set = rait_device_property_set;
165 device_class->recycle_file = rait_device_recycle_file;
166 device_class->finish = rait_device_finish;
167 device_class->read_label = rait_device_read_label;
169 g_object_class->finalize = rait_device_finalize;
171 g_thread_pool_set_max_unused_threads(-1);
174 /* This function does something a little clever and a little
175 * complicated. It takes an array of operations and runs the given
176 * function on each element in the array. The trick is that it runs them
177 * all in parallel, in different threads. This is more efficient than it
178 * sounds because we use a GThreadPool, which means calling this function
179 * will probably not start any new threads at all, but rather use
180 * existing ones. The func is called with two gpointer arguments: The
181 * first from the array, the second is the data argument.
183 * When it returns, all the operations have been successfully
184 * executed. If you want results from your operations, do it yourself
185 * through the array. */
186 static void do_thread_pool_op(GFunc func, GPtrArray * ops, gpointer data) {
190 pool = g_thread_pool_new(func, data, -1, FALSE, NULL);
191 for (i = 0; i < ops->len; i ++) {
192 g_thread_pool_push(pool, g_ptr_array_index(ops, i), NULL);
195 g_thread_pool_free(pool, FALSE, TRUE);
198 /* This does the above, in a serial fashion (and without using threads) */
199 static void do_unthreaded_ops(GFunc func, GPtrArray * ops,
200 gpointer data G_GNUC_UNUSED) {
203 for (i = 0; i < ops->len; i ++) {
204 func(g_ptr_array_index(ops, i), NULL);
208 /* This is the one that code below should call. It switches
209 automatically between do_thread_pool_op and do_unthreaded_ops,
210 depending on g_thread_supported(). */
211 static void do_rait_child_ops(GFunc func, GPtrArray * ops, gpointer data) {
212 if (g_thread_supported()) {
213 do_thread_pool_op(func, ops, data);
215 do_unthreaded_ops(func, ops, data);
219 /* Take a text string user_name, and break it out into an argv-style
220 array of strings. For example, {foo,{bar,baz},bat} would return the
221 strings "foo", "{bar,baz}", "bat", and NULL. Returns NULL on
223 static char ** parse_device_name(char * user_name) {
225 char * cur_end = user_name;
226 char * cur_begin = user_name;
228 rval = g_ptr_array_new();
230 /* Check opening brace. */
231 if (*cur_begin != '{')
239 g_ptr_array_add(rval, g_strndup(cur_begin, cur_end - cur_begin));
246 /* We read until the matching closing brace. */
247 while (*cur_end != '}' && *cur_end != '\0')
254 g_ptr_array_add(rval, g_strndup(cur_begin, cur_end - cur_begin));
255 goto OUTER_END; /* break loop, not switch */
258 /* Unexpected NULL; abort. */
259 g_fprintf(stderr, "Invalid RAIT device name %s\n", user_name);
260 g_ptr_array_free_full(rval);
267 g_assert_not_reached();
271 if (cur_end[1] != '\0') {
272 g_fprintf(stderr, "Invalid RAIT device name %s\n", user_name);
273 g_ptr_array_free_full(rval);
277 g_ptr_array_add(rval, NULL);
279 return (char**) g_ptr_array_free(rval, FALSE);
282 /* Find a workable block size. */
283 static gboolean find_block_size(RaitDevice * self) {
285 uint max = G_MAXUINT;
292 for (i = 0; i < self->private->children->len; i ++) {
293 uint child_min, child_max;
294 GValue property_result;
295 bzero(&property_result, sizeof(property_result));
297 if (!device_property_get(g_ptr_array_index(self->private->children, i),
298 PROPERTY_MIN_BLOCK_SIZE, &property_result))
300 child_min = g_value_get_uint(&property_result);
301 g_return_val_if_fail(child_min > 0, FALSE);
302 if (!device_property_get(g_ptr_array_index(self->private->children, i),
303 PROPERTY_MAX_BLOCK_SIZE, &property_result))
305 child_max = g_value_get_uint(&property_result);
306 g_return_val_if_fail(child_max > 0, FALSE);
308 if (child_min > max || child_max < min || child_min == 0) {
311 min = MAX(min, child_min);
312 max = MIN(max, child_max);
316 /* Now pick a number. */
317 g_assert(min <= max);
318 if (max < MAX_TAPE_BLOCK_BYTES)
320 else if (min > MAX_TAPE_BLOCK_BYTES)
323 result = MAX_TAPE_BLOCK_BYTES;
325 /* User reads and writes bigger blocks. */
326 find_simple_params(self, NULL, &data_children, NULL);
327 self->private->block_size = result * data_children;
329 bzero(&val, sizeof(val));
330 g_value_init(&val, G_TYPE_INT);
331 g_value_set_int(&val, result);
332 /* We can't do device_property_set because it's disallowed
333 according to the registered property base. */
334 rval = rait_device_property_set(DEVICE(self), PROPERTY_BLOCK_SIZE, &val);
339 /* Register properties that belong to the RAIT device proper, and not
341 static void register_rait_properties(RaitDevice * self) {
342 Device * o = DEVICE(self);
345 prop.access = PROPERTY_ACCESS_GET_MASK;
347 prop.base = &device_property_min_block_size;
348 device_add_property(o, &prop, NULL);
350 prop.base = &device_property_max_block_size;
351 device_add_property(o, &prop, NULL);
353 prop.base = &device_property_block_size;
354 device_add_property(o, &prop, NULL);
356 prop.base = &device_property_canonical_name;
357 device_add_property(o, &prop, NULL);
360 static void property_hash_union(GHashTable * properties,
361 DeviceProperty * prop) {
362 PropertyAccessFlags before, after;
366 found = g_hash_table_lookup_extended(properties,
367 GUINT_TO_POINTER(prop->base->ID),
369 before = GPOINTER_TO_UINT(tmp);
372 after = prop->access;
374 after = before & prop->access;
377 g_hash_table_insert(properties, GUINT_TO_POINTER(prop->base->ID),
378 GUINT_TO_POINTER(after));
382 static gboolean zero_value(gpointer key G_GNUC_UNUSED, gpointer value,
383 gpointer user_data G_GNUC_UNUSED) {
384 return (0 == GPOINTER_TO_UINT(value));
388 static void register_property_hash(gpointer key, gpointer value,
389 gpointer user_data) {
390 DevicePropertyId id = GPOINTER_TO_UINT(key);
392 Device * device = (Device*)user_data;
394 g_assert(IS_DEVICE(device));
396 prop.access = GPOINTER_TO_UINT(value);
397 prop.base = device_property_get_by_id(id);
399 device_add_property(device, &prop, NULL);
402 /* This function figures out which properties exist for all children, and
403 * exports the unioned access mask. */
404 static void register_properties(RaitDevice * self) {
405 GHashTable * properties; /* PropertyID => PropertyAccessFlags */
408 properties = g_hash_table_new(g_direct_hash, g_direct_equal);
410 /* Iterate the device list, find all properties. */
411 for (j = 0; j < self->private->children->len; j ++) {
413 Device * child = g_ptr_array_index(self->private->children, j);
414 const DeviceProperty* device_property_list;
416 device_property_list = device_property_get_list(child);
417 for (i = 0; device_property_list[i].base != NULL; i ++) {
418 property_hash_union(properties, (gpointer)&(device_property_list[i]));
422 /* Then toss properties that can't be accessed. */
423 g_hash_table_foreach_remove(properties, zero_value, NULL);
424 g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_BLOCK_SIZE));
425 g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_MIN_BLOCK_SIZE));
426 g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_MAX_BLOCK_SIZE));
427 g_hash_table_remove(properties, GINT_TO_POINTER(PROPERTY_CANONICAL_NAME));
429 /* Finally, register the lot. */
430 g_hash_table_foreach(properties, register_property_hash, self);
432 g_hash_table_destroy(properties);
434 /* Then we have some of our own properties to register. */
435 register_rait_properties(self);
438 /* This structure contains common fields for many operations. Not all
439 operations use all fields, however. */
441 gpointer result; /* May be a pointer; may be an integer or boolean
442 stored with GINT_TO_POINTER. */
443 Device * child; /* The device in question. Used by all
445 guint child_index; /* For recoverable operations (read-related
446 operations), this field provides the number
447 of this child in the self->private->children
451 typedef gboolean (*BooleanExtractor)(gpointer data);
453 /* A BooleanExtractor */
454 static gboolean extract_boolean_generic_op(gpointer data) {
455 GenericOp * op = data;
456 return GPOINTER_TO_INT(op->result);
459 /* A BooleanExtractor */
460 static gboolean extract_boolean_pointer_op(gpointer data) {
461 GenericOp * op = data;
462 return op->result != NULL;
465 /* Does the equivalent of this perl command:
466 ! (first { !extractor($_) } @_
467 That is, calls extractor on each element of the array, and returns
468 TRUE if and only if all calls to extractor return TRUE.
470 static gboolean g_ptr_array_and(GPtrArray * array,
471 BooleanExtractor extractor) {
473 if (array == NULL || array->len <= 0)
476 for (i = 0; i < array->len; i ++) {
477 if (!extractor(g_ptr_array_index(array, i)))
484 /* Takes a RaitDevice, and makes a GPtrArray of GenericOp. */
485 static GPtrArray * make_generic_boolean_op_array(RaitDevice* self) {
489 rval = g_ptr_array_sized_new(self->private->children->len);
490 for (i = 0; i < self->private->children->len; i ++) {
492 op = malloc(sizeof(*op));
493 op->child = g_ptr_array_index(self->private->children, i);
495 g_ptr_array_add(rval, op);
501 /* Takes a GPtrArray of GenericOp, and a BooleanExtractor, and does
502 all the proper handling for the result of operations that allow
503 device isolation. Returns FALSE only if an unrecoverable error
505 static gboolean g_ptr_array_union_robust(RaitDevice * self, GPtrArray * ops,
506 BooleanExtractor extractor) {
508 gpointer isolated_op = NULL;
511 success = g_ptr_array_and(ops, extractor);
513 if (success || self->private->status != RAIT_STATUS_COMPLETE) {
517 /* First device failure, note the device and march on. */
518 self->private->status = RAIT_STATUS_DEGRADED;
519 for (i = 0; i < ops->len; i ++) {
520 GenericOp * op = g_ptr_array_index(ops, i);
522 isolated_op = g_ptr_array_remove_index_fast(ops, i);
523 self->private->failed = op->child_index;
524 g_fprintf(stderr, "RAIT array %s Isolated device %s.\n",
525 DEVICE(self)->device_name,
526 op->child->device_name);
533 /* Return isolated op so any data members can be freed. */
534 if (isolated_op != NULL) {
535 g_ptr_array_add(ops, isolated_op);
541 Device * result; /* IN */
542 char * device_name; /* OUT */
546 static void open_device_do_op(gpointer data,
547 gpointer user_data G_GNUC_UNUSED) {
548 OpenDeviceOp * op = data;
550 op->result = device_open(op->device_name);
551 amfree(op->device_name);
554 /* Returns TRUE if and only if the volume label and time are equal. */
555 static gboolean compare_volume_results(Device * a, Device * b) {
556 if (a->volume_time != b->volume_time)
558 if (a->volume_label == NULL && b->volume_label == NULL)
560 if (a->volume_label == NULL || b->volume_label == NULL)
562 return 0 == strcmp(a->volume_label, b->volume_label);
566 rait_device_open_device (Device * dself, char * device_name) {
567 char ** device_names;
568 GPtrArray * open_device_ops;
573 self = RAIT_DEVICE(dself);
574 g_return_val_if_fail(self != NULL, FALSE);
575 g_return_val_if_fail (device_name != NULL, FALSE);
577 device_names = parse_device_name(device_name);
579 if (device_names == NULL)
582 /* Open devices in a separate thread, in case they have to rewind etc. */
583 open_device_ops = g_ptr_array_new();
585 for (i = 0; device_names[i] != NULL; i ++) {
588 op = malloc(sizeof(*op));
589 op->device_name = device_names[i];
591 g_ptr_array_add(open_device_ops, op);
595 do_rait_child_ops(open_device_do_op, open_device_ops, NULL);
598 /* Check results of opening devices. */
599 for (i = 0; i < open_device_ops->len; i ++) {
600 OpenDeviceOp *op = g_ptr_array_index(open_device_ops, i);
602 if (op->result != NULL) {
603 g_ptr_array_add(self->private->children, op->result);
609 g_ptr_array_free_full(open_device_ops);
611 failure = failure || !find_block_size(self);
613 return FALSE; /* This will clean up any created children. */
615 register_properties(self);
618 if (parent_class->open_device) {
619 return parent_class->open_device(dself, device_name);
626 static void read_label_do_op(gpointer data,
627 gpointer user_data G_GNUC_UNUSED) {
628 GenericOp * op = data;
629 op->result = GINT_TO_POINTER(device_read_label(op->child));
632 static ReadLabelStatusFlags rait_device_read_label(Device * dself) {
635 ReadLabelStatusFlags failed_result = 0;
636 ReadLabelStatusFlags rval;
637 GenericOp * failed_op = NULL; /* If this is non-null, we will isolate. */
639 Device * first_success = NULL;
641 self = RAIT_DEVICE(dself);
642 g_return_val_if_fail(self != NULL, FALSE);
644 amfree(dself->volume_label);
646 ops = make_generic_boolean_op_array(self);
648 do_rait_child_ops(read_label_do_op, ops, NULL);
650 for (i = 0; i < ops->len; i ++) {
651 GenericOp * op = g_ptr_array_index(ops, i);
652 ReadLabelStatusFlags result = GPOINTER_TO_INT(op->result);
653 if (op->result == READ_LABEL_STATUS_SUCCESS) {
654 if (first_success == NULL) {
655 /* This is the first successful device. */
656 first_success = op->child;
657 } else if (!compare_volume_results(first_success, op->child)) {
658 /* Doesn't match. :-( */
659 g_fprintf(stderr, "Inconsistant volume labels: "
660 "Got %s/%s against %s/%s.\n",
661 first_success->volume_label,
662 first_success->volume_time,
663 op->child->volume_label,
664 op->child->volume_time);
665 failed_result |= READ_LABEL_STATUS_VOLUME_ERROR;
669 if (failed_result == 0 &&
670 self->private->status == RAIT_STATUS_COMPLETE) {
671 /* This is the first failed device; note it and we'll isolate
674 failed_result = result;
676 /* We've encountered multiple failures. OR them together. */
677 failed_result |= result;
683 if (failed_op != NULL) {
684 /* We have a single device to isolate. */
685 failed_result = READ_LABEL_STATUS_SUCCESS; /* Recover later */
686 self->private->failed = failed_op->child_index;
687 g_fprintf(stderr, "RAIT array %s Isolated device %s.\n",
689 failed_op->child->device_name);
692 if (failed_result != READ_LABEL_STATUS_SUCCESS) {
693 /* We had multiple failures or an inconsistency. */
694 rval = failed_result;
696 /* Everything peachy. */
697 rval = READ_LABEL_STATUS_SUCCESS;
698 g_assert(first_success != NULL);
699 if (first_success->volume_label != NULL) {
700 dself->volume_label = g_strdup(first_success->volume_label);
702 if (first_success->volume_time != NULL) {
703 dself->volume_time = g_strdup(first_success->volume_time);
707 g_ptr_array_free_full(ops);
714 DeviceAccessMode mode; /* IN */
715 char * label; /* IN */
716 char * timestamp; /* IN */
720 static void start_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
722 StartOp * param = data;
724 klass = DEVICE_GET_CLASS(param->base.child);
727 GINT_TO_POINTER((klass->start)(param->base.child,
728 param->mode, param->label,
731 param->base.result = FALSE;
736 rait_device_start (Device * dself, DeviceAccessMode mode, char * label,
743 self = RAIT_DEVICE(dself);
744 g_return_val_if_fail(self != NULL, FALSE);
746 ops = g_ptr_array_sized_new(self->private->children->len);
747 for (i = 0; i < self->private->children->len; i ++) {
749 op = malloc(sizeof(*op));
750 op->base.child = g_ptr_array_index(self->private->children, i);
752 op->label = g_strdup(label);
753 op->timestamp = g_strdup(timestamp);
754 g_ptr_array_add(ops, op);
757 do_rait_child_ops(start_do_op, ops, NULL);
759 success = g_ptr_array_and(ops, extract_boolean_generic_op);
761 g_ptr_array_free_full(ops);
765 } else if (parent_class->start) {
766 return parent_class->start(dself, mode, label, timestamp);
774 const dumpfile_t * info; /* IN */
778 static void start_file_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
779 StartFileOp * op = data;
780 op->base.result = GINT_TO_POINTER(device_start_file(op->base.child,
785 rait_device_start_file (Device * dself, const dumpfile_t * info) {
791 self = RAIT_DEVICE(dself);
792 g_return_val_if_fail(self != NULL, FALSE);
794 ops = g_ptr_array_sized_new(self->private->children->len);
795 for (i = 0; i < self->private->children->len; i ++) {
797 op = malloc(sizeof(*op));
798 op->base.child = g_ptr_array_index(self->private->children, i);
800 g_ptr_array_add(ops, op);
803 do_rait_child_ops(start_file_do_op, ops, NULL);
805 success = g_ptr_array_and(ops, extract_boolean_generic_op);
807 g_ptr_array_free_full(ops);
811 } else if (parent_class->start_file) {
812 return parent_class->start_file(dself, info);
818 static void find_simple_params(RaitDevice * self,
819 guint * num_children,
820 guint * data_children,
824 num = self->private->children->len;
829 if (num_children != NULL)
831 if (data_children != NULL)
832 *data_children = data;
834 if (blocksize != NULL) {
835 *blocksize = device_write_min_size(DEVICE(self));
842 gpointer data; /* IN */
843 gboolean short_block; /* IN */
844 gboolean data_needs_free; /* bookkeeping */
848 static void write_block_do_op(gpointer data,
849 gpointer user_data G_GNUC_UNUSED) {
850 WriteBlockOp * op = data;
853 GINT_TO_POINTER(device_write_block(op->base.child, op->size, op->data,
857 /* Parity block generation. Performance of this function can be improved
858 considerably by using larger-sized integers or
859 assembly-coded vector instructions. Parameters are:
860 % data - All data chunks in series (chunk_size * num_chunks bytes)
861 % parity - Allocated space for parity block (chunk_size bytes)
863 static void make_parity_block(char * data, char * parity,
864 guint chunk_size, guint num_chunks) {
866 bzero(parity, chunk_size);
867 for (i = 0; i < num_chunks - 1; i ++) {
869 for (j = 0; j < chunk_size; j ++) {
870 parity[j] ^= data[chunk_size*i + j];
875 /* Does the same thing as make_parity_block, but instead of using a
876 single memory chunk holding all chunks, it takes a GPtrArray of
878 static void make_parity_block_extents(GPtrArray * data, char * parity,
881 bzero(parity, chunk_size);
882 for (i = 0; i < data->len; i ++) {
885 data_chunk = g_ptr_array_index(data, i);
886 for (j = 0; j < chunk_size; j ++) {
887 parity[j] ^= data_chunk[j];
892 /* Does the parity creation algorithm. Allocates and returns a single
893 device block from a larger RAIT block. chunks and chunk are 1-indexed. */
894 static char * extract_data_block(char * data, guint size,
895 guint chunks, guint chunk) {
899 g_return_val_if_fail(chunks > 0 && chunk > 0 && chunk <= chunks, NULL);
900 g_return_val_if_fail(data != NULL, NULL);
901 g_return_val_if_fail(size > 0 && size % (chunks - 1) == 0, NULL);
903 chunk_size = size / (chunks - 1);
904 rval = malloc(chunk_size);
905 if (chunks != chunk) {
907 memcpy(rval, data + chunk_size * (chunk - 1), chunk_size);
909 make_parity_block(data, rval, chunk_size, chunks);
916 rait_device_write_block (Device * dself, guint size, gpointer data,
917 gboolean last_block) {
921 guint data_children, num_children;
925 self = RAIT_DEVICE(dself);
926 g_return_val_if_fail(self != NULL, FALSE);
928 find_simple_params(RAIT_DEVICE(self), &num_children, &data_children,
930 num_children = self->private->children->len;
931 if (num_children != 1)
932 data_children = num_children - 1;
934 data_children = num_children;
936 g_return_val_if_fail(size % data_children == 0 || last_block, FALSE);
941 new_data = malloc(blocksize);
942 memcpy(new_data, data, size);
943 bzero(new_data + size, blocksize - size);
949 ops = g_ptr_array_sized_new(num_children);
950 for (i = 0; i < self->private->children->len; i ++) {
952 op = malloc(sizeof(*op));
953 op->base.child = g_ptr_array_index(self->private->children, i);
954 op->short_block = last_block;
955 op->size = size / data_children;
956 if (num_children <= 2) {
958 op->data_needs_free = FALSE;
960 op->data_needs_free = TRUE;
961 op->data = extract_data_block(data, size, num_children, i + 1);
963 g_ptr_array_add(ops, op);
970 do_rait_child_ops(write_block_do_op, ops, NULL);
972 success = g_ptr_array_and(ops, extract_boolean_generic_op);
974 for (i = 0; i < self->private->children->len; i ++) {
975 WriteBlockOp * op = g_ptr_array_index(ops, i);
976 if (op->data_needs_free)
980 g_ptr_array_free_full(ops);
985 /* We don't chain up here because we must handle finish_file
986 differently. If we were called with last_block, then the
987 children have already called finish_file themselves. So we
988 update the device block numbers manually. */
991 dself->in_file = FALSE;
998 static void finish_file_do_op(gpointer data,
999 gpointer user_data G_GNUC_UNUSED) {
1000 GenericOp * op = data;
1001 op->result = GINT_TO_POINTER(device_finish_file(op->child));
1005 rait_device_finish_file (Device * self) {
1009 ops = make_generic_boolean_op_array(RAIT_DEVICE(self));
1011 do_rait_child_ops(finish_file_do_op, ops, NULL);
1013 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1015 g_ptr_array_free_full(ops);
1019 } else if (parent_class->finish_file) {
1020 return parent_class->finish_file(self);
1028 guint requested_file; /* IN */
1029 guint actual_file; /* OUT */
1033 static void seek_file_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1034 SeekFileOp * op = data;
1035 op->base.result = device_seek_file(op->base.child, op->requested_file);
1036 op->actual_file = op->base.child->file;
1040 rait_device_seek_file (Device * dself, guint file) {
1045 RaitDevice * self = RAIT_DEVICE(dself);
1046 guint actual_file = 0;
1047 g_return_val_if_fail(self != NULL, FALSE);
1049 ops = g_ptr_array_sized_new(self->private->children->len);
1050 for (i = 0; i < self->private->children->len; i ++) {
1052 if ((int)i == self->private->failed)
1053 continue; /* This device is broken. */
1054 op = malloc(sizeof(*op));
1055 op->base.child = g_ptr_array_index(self->private->children, i);
1056 op->base.child_index = i;
1057 op->requested_file = file;
1058 g_ptr_array_add(ops, op);
1061 do_rait_child_ops(seek_file_do_op, ops, NULL);
1063 /* This checks for NULL values, but we still have to check for
1064 consistant headers. */
1065 success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1066 ops, extract_boolean_pointer_op);
1069 for (i = 0; i < self->private->children->len; i ++) {
1070 SeekFileOp * this_op;
1071 dumpfile_t * this_result;
1072 guint this_actual_file;
1073 if ((int)i == self->private->failed)
1076 this_op = (SeekFileOp*)g_ptr_array_index(ops, i);
1077 this_result = this_op->base.result;
1078 this_actual_file = this_op->actual_file;
1082 actual_file = this_actual_file;
1084 if (headers_are_equal(rval, this_result) &&
1085 actual_file == this_actual_file) {
1094 g_ptr_array_free_full(ops);
1099 } else if (parent_class->seek_file) {
1100 parent_class->seek_file(dself, file);
1108 guint64 block; /* IN */
1112 static void seek_block_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1113 SeekBlockOp * op = data;
1115 GINT_TO_POINTER(device_seek_block(op->base.child, op->block));
1119 rait_device_seek_block (Device * dself, guint64 block) {
1124 RaitDevice * self = RAIT_DEVICE(dself);
1125 g_return_val_if_fail(self != NULL, FALSE);
1127 ops = g_ptr_array_sized_new(self->private->children->len);
1128 for (i = 0; i < self->private->children->len; i ++) {
1130 if ((int)i == self->private->failed)
1131 continue; /* This device is broken. */
1132 op = malloc(sizeof(*op));
1133 op->base.child = g_ptr_array_index(self->private->children, i);
1134 op->base.child_index = i;
1136 g_ptr_array_add(ops, op);
1139 do_rait_child_ops(seek_block_do_op, ops, NULL);
1141 success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1142 ops, extract_boolean_generic_op);
1144 g_ptr_array_free_full(ops);
1148 } else if (parent_class->seek_block) {
1149 return parent_class->seek_block(dself, block);
1157 gpointer buffer; /* IN */
1158 int read_size; /* IN/OUT -- note not a pointer */
1159 int desired_read_size; /* bookkeeping */
1163 static void read_block_do_op(gpointer data,
1164 gpointer user_data G_GNUC_UNUSED) {
1165 ReadBlockOp * op = data;
1167 GINT_TO_POINTER(device_read_block(op->base.child, op->buffer,
1171 /* A BooleanExtractor. This one checks for a successful read. */
1172 static gboolean extract_boolean_read_block_op_data(gpointer data) {
1173 ReadBlockOp * op = data;
1174 return GPOINTER_TO_INT(op->base.result) == op->desired_read_size;
1177 /* A BooleanExtractor. This one checks for EOF. */
1178 static gboolean extract_boolean_read_block_op_eof(gpointer data) {
1179 ReadBlockOp * op = data;
1180 return op->base.child->is_eof;
1183 static int g_ptr_array_count(GPtrArray * array, BooleanExtractor filter) {
1187 for (i = 0; i < array->len ; i++) {
1188 if (filter(g_ptr_array_index(array, i)))
1194 static gboolean raid_block_reconstruction(RaitDevice * self, GPtrArray * ops,
1196 guint num_children, data_children;
1197 int blocksize, child_blocksize;
1200 gpointer parity_block = NULL;
1204 find_simple_params(self, &num_children, &data_children, &blocksize);
1205 if (num_children > 1)
1206 parity_child = num_children - 1;
1210 child_blocksize = blocksize / data_children;
1212 for (i = 0; i < ops->len; i ++) {
1213 ReadBlockOp * op = g_ptr_array_index(ops, i);
1214 if (!extract_boolean_read_block_op_data(op))
1216 if ((int)(op->base.child_index) == parity_child) {
1217 parity_block = op->buffer;
1219 memcpy((char *)buf + child_blocksize * op->base.child_index, op->buffer,
1224 if (self->private->status == RAIT_STATUS_COMPLETE) {
1225 if (num_children >= 2) {
1226 /* Verify the parity block. This code is inefficient but
1227 does the job for the 2-device case, too. */
1228 gpointer constructed_parity;
1229 GPtrArray * data_extents;
1231 constructed_parity = malloc(child_blocksize);
1232 data_extents = g_ptr_array_sized_new(data_children);
1233 for (i = 0; i < data_children; i ++) {
1234 ReadBlockOp * op = g_ptr_array_index(ops, i);
1235 g_assert(extract_boolean_read_block_op_data(op));
1236 if ((int)op->base.child_index == parity_child)
1238 g_ptr_array_add(data_extents, op->buffer);
1240 make_parity_block_extents(data_extents, constructed_parity,
1243 if (0 != memcmp(parity_block, constructed_parity,
1245 g_fprintf(stderr, "RAIT is inconsistant: "
1246 "Parity block did not match data blocks.\n");
1249 g_ptr_array_free(data_extents, TRUE);
1250 amfree(constructed_parity);
1251 } else { /* do nothing. */ }
1252 } else if (self->private->status == RAIT_STATUS_DEGRADED) {
1253 /* We are in degraded mode. What's missing? */
1254 if (self->private->failed == parity_child) {
1256 } else if (num_children >= 2) {
1257 /* Reconstruct failed block from parity block. */
1258 GPtrArray * data_extents = g_ptr_array_new();
1260 for (i = 0; i < data_children; i ++) {
1261 ReadBlockOp * op = g_ptr_array_index(ops, i);
1262 if (!extract_boolean_read_block_op_data(op))
1264 g_ptr_array_add(data_extents, op->buffer);
1267 /* Conveniently, the reconstruction is the same procedure
1268 as the parity generation. This even works if there is
1269 only one remaining device! */
1270 make_parity_block_extents(data_extents,
1271 (char *)buf + (child_blocksize *
1272 self->private->failed),
1275 /* The array members belong to our ops argument. */
1276 g_ptr_array_free(data_extents, TRUE);
1278 g_assert_not_reached();
1287 rait_device_read_block (Device * dself, gpointer buf, int * size) {
1291 guint num_children, data_children;
1294 RaitDevice * self = RAIT_DEVICE(dself);
1295 g_return_val_if_fail(self != NULL, -1);
1297 find_simple_params(self, &num_children, &data_children,
1300 g_return_val_if_fail(*size >= (int)device_write_min_size(dself), -1);
1301 g_assert(blocksize % data_children == 0); /* If not we are screwed */
1304 ops = g_ptr_array_sized_new(num_children);
1305 for (i = 0; i < num_children; i ++) {
1307 if ((int)i == self->private->failed)
1308 continue; /* This device is broken. */
1309 op = malloc(sizeof(*op));
1310 op->base.child = g_ptr_array_index(self->private->children, i);
1311 op->base.child_index = i;
1312 op->buffer = malloc(blocksize / data_children);
1313 op->desired_read_size = op->read_size = blocksize / data_children;
1314 g_ptr_array_add(ops, op);
1317 do_rait_child_ops(read_block_do_op, ops, NULL);
1319 if (g_ptr_array_count(ops, extract_boolean_read_block_op_data)) {
1321 g_ptr_array_union_robust(RAIT_DEVICE(self),
1323 extract_boolean_read_block_op_data) &&
1324 raid_block_reconstruction(RAIT_DEVICE(self),
1328 if (g_ptr_array_union_robust(RAIT_DEVICE(self),
1330 extract_boolean_read_block_op_eof)) {
1332 dself->is_eof = TRUE;
1336 for (i = 0; i < ops->len; i ++) {
1337 ReadBlockOp * op = g_ptr_array_index(ops, i);
1340 g_ptr_array_free_full(ops);
1343 if (parent_class->read_block)
1344 parent_class->read_block(dself, buf, size);
1353 DevicePropertyId id; /* IN */
1354 GValue value; /* IN/OUT */
1355 gboolean label_changed; /* Did the device label change? OUT; _set only*/
1358 /* Creates a GPtrArray of PropertyOf for a get or set operation. */
1359 static GPtrArray * make_property_op_array(RaitDevice * self,
1360 DevicePropertyId id,
1364 ops = g_ptr_array_sized_new(self->private->children->len);
1365 for (i = 0; i < self->private->children->len; i ++) {
1367 op = malloc(sizeof(*op));
1368 op->base.child = g_ptr_array_index(self->private->children, i);
1370 bzero(&(op->value), sizeof(op->value));
1371 if (value != NULL) {
1372 g_value_unset_copy(value, &(op->value));
1374 g_ptr_array_add(ops, op);
1381 static void property_get_do_op(gpointer data,
1382 gpointer user_data G_GNUC_UNUSED) {
1383 PropertyOp * op = data;
1385 bzero(&(op->value), sizeof(op->value));
1387 GINT_TO_POINTER(device_property_get(op->base.child, op->id,
1391 /* Merge ConcurrencyParadigm results. */
1392 static gboolean property_get_concurrency(GPtrArray * ops, GValue * val) {
1393 ConcurrencyParadigm result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
1396 for (i = 0; i < ops->len; i ++) {
1397 ConcurrencyParadigm cur;
1398 PropertyOp * op = g_ptr_array_index(ops, i);
1399 g_return_val_if_fail(G_VALUE_TYPE(&(op->value)) ==
1400 CONCURRENCY_PARADIGM_TYPE, FALSE);
1401 cur = g_value_get_enum(&(op->value));
1402 if (result == CONCURRENCY_PARADIGM_EXCLUSIVE ||
1403 cur == CONCURRENCY_PARADIGM_EXCLUSIVE) {
1404 result = CONCURRENCY_PARADIGM_EXCLUSIVE;
1405 } else if (result == CONCURRENCY_PARADIGM_SHARED_READ ||
1406 cur == CONCURRENCY_PARADIGM_SHARED_READ) {
1407 result = CONCURRENCY_PARADIGM_SHARED_READ;
1408 } else if (result == CONCURRENCY_PARADIGM_RANDOM_ACCESS &&
1409 cur == CONCURRENCY_PARADIGM_RANDOM_ACCESS) {
1410 result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
1412 g_return_val_if_fail(FALSE, FALSE);
1416 g_value_unset_init(val, CONCURRENCY_PARADIGM_TYPE);
1417 g_value_set_enum(val, result);
1421 /* Merge StreamingRequirement results. */
1422 static gboolean property_get_streaming(GPtrArray * ops, GValue * val) {
1423 StreamingRequirement result = STREAMING_REQUIREMENT_NONE;
1426 for (i = 0; i < ops->len; i ++) {
1427 StreamingRequirement cur;
1428 PropertyOp * op = g_ptr_array_index(ops, i);
1429 g_return_val_if_fail(G_VALUE_TYPE(&(op->value)) ==
1430 STREAMING_REQUIREMENT_TYPE, FALSE);
1431 cur = g_value_get_enum(&(op->value));
1432 if (result == STREAMING_REQUIREMENT_REQUIRED ||
1433 cur == STREAMING_REQUIREMENT_REQUIRED) {
1434 result = STREAMING_REQUIREMENT_REQUIRED;
1435 } else if (result == STREAMING_REQUIREMENT_DESIRED ||
1436 cur == STREAMING_REQUIREMENT_DESIRED) {
1437 result = STREAMING_REQUIREMENT_DESIRED;
1438 } else if (result == STREAMING_REQUIREMENT_NONE &&
1439 cur == STREAMING_REQUIREMENT_NONE) {
1440 result = STREAMING_REQUIREMENT_NONE;
1442 g_return_val_if_fail(FALSE, FALSE);
1446 g_value_unset_init(val, STREAMING_REQUIREMENT_TYPE);
1447 g_value_set_enum(val, result);
1451 /* Merge MediaAccessMode results. */
1452 static gboolean property_get_medium_type(GPtrArray * ops, GValue * val) {
1453 MediaAccessMode result = 0;
1456 for (i = 0; i < ops->len; i ++) {
1457 MediaAccessMode cur;
1458 PropertyOp * op = g_ptr_array_index(ops, i);
1459 g_return_val_if_fail(G_VALUE_TYPE(&(op->value)) ==
1460 MEDIA_ACCESS_MODE_TYPE, FALSE);
1461 cur = g_value_get_enum(&(op->value));
1465 } else if ((result == MEDIA_ACCESS_MODE_READ_ONLY &&
1466 cur == MEDIA_ACCESS_MODE_WRITE_ONLY) ||
1467 (result == MEDIA_ACCESS_MODE_WRITE_ONLY &&
1468 cur == MEDIA_ACCESS_MODE_READ_ONLY)) {
1469 /* Invalid combination; one device can only read, other
1472 } else if (result == MEDIA_ACCESS_MODE_READ_ONLY ||
1473 cur == MEDIA_ACCESS_MODE_READ_ONLY) {
1474 result = MEDIA_ACCESS_MODE_READ_ONLY;
1475 } else if (result == MEDIA_ACCESS_MODE_WRITE_ONLY ||
1476 cur == MEDIA_ACCESS_MODE_WRITE_ONLY) {
1477 result = MEDIA_ACCESS_MODE_WRITE_ONLY;
1478 } else if (result == MEDIA_ACCESS_MODE_WORM ||
1479 cur == MEDIA_ACCESS_MODE_WORM) {
1480 result = MEDIA_ACCESS_MODE_WORM;
1481 } else if (result == MEDIA_ACCESS_MODE_READ_WRITE &&
1482 cur == MEDIA_ACCESS_MODE_READ_WRITE) {
1483 result = MEDIA_ACCESS_MODE_READ_WRITE;
1485 g_return_val_if_fail(FALSE, FALSE);
1489 g_value_unset_init(val, MEDIA_ACCESS_MODE_TYPE);
1490 g_value_set_enum(val, result);
1494 /* Merge QualifiedSize results. */
1495 static gboolean property_get_free_space(GPtrArray * ops, GValue * val) {
1496 QualifiedSize result;
1499 for (i = 0; i < ops->len; i ++) {
1501 PropertyOp * op = g_ptr_array_index(ops, i);
1502 g_return_val_if_fail(G_VALUE_TYPE(&(op->value)) ==
1503 QUALIFIED_SIZE_TYPE, FALSE);
1504 cur = *(QualifiedSize*)(g_value_get_boxed(&(op->value)));
1506 if (result.accuracy != cur.accuracy) {
1507 result.accuracy = SIZE_ACCURACY_ESTIMATE;
1510 if (result.accuracy == SIZE_ACCURACY_UNKNOWN &&
1511 cur.accuracy != SIZE_ACCURACY_UNKNOWN) {
1512 result.bytes = cur.bytes;
1513 } else if (result.accuracy != SIZE_ACCURACY_UNKNOWN &&
1514 cur.accuracy == SIZE_ACCURACY_UNKNOWN) {
1515 /* result.bytes unchanged. */
1517 result.bytes = MIN(result.bytes, cur.bytes);
1521 g_value_unset_init(val, QUALIFIED_SIZE_TYPE);
1522 g_value_set_boxed(val, &result);
1526 /* Merge boolean results by ANDing them together. */
1527 static gboolean property_get_boolean_and(GPtrArray * ops, GValue * val) {
1528 gboolean result = FALSE;
1531 for (i = 0; i < ops->len; i ++) {
1533 PropertyOp * op = g_ptr_array_index(ops, i);
1534 g_return_val_if_fail(G_VALUE_HOLDS_BOOLEAN(&(op->value)), FALSE);
1535 cur = g_value_get_boolean(&(op->value));
1537 result = result && cur;
1540 g_value_unset_init(val, G_TYPE_BOOLEAN);
1541 g_value_set_boolean(val, result);
1547 rait_device_property_get (Device * dself, DevicePropertyId id, GValue * val) {
1552 GValue * first_value;
1553 RaitDevice * self = RAIT_DEVICE(dself);
1554 g_return_val_if_fail(self != NULL, FALSE);
1556 /* Some properties are handled completely differently. */
1557 if (id == PROPERTY_BLOCK_SIZE) {
1558 g_value_unset_init(val, G_TYPE_INT);
1559 g_value_set_int(val, self->private->block_size);
1561 } else if (id == PROPERTY_MIN_BLOCK_SIZE ||
1562 id == PROPERTY_MAX_BLOCK_SIZE) {
1563 g_value_unset_init(val, G_TYPE_UINT);
1564 g_value_set_uint(val, self->private->block_size);
1566 } else if (id == PROPERTY_CANONICAL_NAME) {
1567 if (parent_class->property_get != NULL) {
1568 return parent_class->property_get(dself, id, val);
1574 ops = make_property_op_array(self, id, NULL);
1576 do_rait_child_ops(property_get_do_op, ops, NULL);
1578 if (id == PROPERTY_CONCURRENCY) {
1579 success = property_get_concurrency(ops, val);
1580 } else if (id == PROPERTY_STREAMING) {
1581 success = property_get_streaming(ops, val);
1582 } else if (id == PROPERTY_APPENDABLE ||
1583 id == PROPERTY_PARTIAL_DELETION) {
1584 success = property_get_boolean_and(ops, val);
1585 } else if (id == PROPERTY_MEDIUM_TYPE) {
1586 success = property_get_medium_type(ops, val);
1587 } else if (id == PROPERTY_FREE_SPACE) {
1588 success = property_get_free_space(ops, val);
1590 /* Generic handling; if all results are the same, we succeed
1591 and return that result. If not, we fail. */
1594 /* Set up comparison value. */
1595 bzero(&result, sizeof(result));
1596 first_value = &(((PropertyOp*)g_ptr_array_index(ops,0))->value);
1597 if (G_IS_VALUE(first_value)) {
1598 g_value_unset_copy(first_value, &result);
1603 for (i = 0; i < ops->len; i ++) {
1604 PropertyOp * op = g_ptr_array_index(ops, i);
1605 if (!GPOINTER_TO_INT(op->base.result) ||
1606 !G_IS_VALUE(first_value) ||
1607 !g_value_compare(&result, &(op->value))) {
1610 g_value_unset(&(op->value));
1614 memcpy(val, &result, sizeof(result));
1615 } else if (G_IS_VALUE(&result)) {
1616 g_value_unset(&result);
1620 g_ptr_array_free_full(ops);
1626 static void property_set_do_op(gpointer data,
1627 gpointer user_data G_GNUC_UNUSED) {
1628 PropertyOp * op = data;
1629 gboolean label_set = (op->base.child->volume_label != NULL);
1631 GINT_TO_POINTER(device_property_set(op->base.child, op->id,
1633 op->label_changed = (label_set != (op->base.child->volume_label != NULL));
1636 /* A BooleanExtractor */
1637 static gboolean extract_label_changed_property_op(gpointer data) {
1638 PropertyOp * op = data;
1639 return op->label_changed;
1643 static void clear_volume_details_do_op(gpointer data,
1644 gpointer user_data G_GNUC_UNUSED) {
1645 GenericOp * op = data;
1646 device_clear_volume_details(op->child);
1650 rait_device_property_set (Device * d_self, DevicePropertyId id, GValue * val) {
1654 gboolean label_changed;
1656 self = RAIT_DEVICE(d_self);
1657 g_return_val_if_fail(self != NULL, FALSE);
1659 ops = make_property_op_array(self, id, val);
1661 do_rait_child_ops(property_set_do_op, ops, NULL);
1663 success = g_ptr_array_union_robust(self, ops, extract_boolean_generic_op);
1665 g_ptr_array_union_robust(self, ops,
1666 extract_label_changed_property_op);
1667 g_ptr_array_free_full(ops);
1669 if (label_changed) {
1670 /* At least one device considered this property set a label-changing
1671 * operation, so now we clear labels on all devices. */
1672 ops = make_generic_boolean_op_array(self);
1673 do_rait_child_ops(clear_volume_details_do_op, ops, NULL);
1674 g_ptr_array_free_full(ops);
1686 static void recycle_file_do_op(gpointer data,
1687 gpointer user_data G_GNUC_UNUSED) {
1688 RecycleFileOp * op = data;
1690 GINT_TO_POINTER(device_recycle_file(op->base.child, op->filenum));
1694 rait_device_recycle_file (Device * dself, guint filenum) {
1699 RaitDevice * self = RAIT_DEVICE(dself);
1700 g_return_val_if_fail(self != NULL, FALSE);
1702 ops = g_ptr_array_sized_new(self->private->children->len);
1703 for (i = 0; i < self->private->children->len; i ++) {
1705 op = malloc(sizeof(*op));
1706 op->base.child = g_ptr_array_index(self->private->children, i);
1707 op->filenum = filenum;
1708 g_ptr_array_add(ops, op);
1711 do_rait_child_ops(recycle_file_do_op, ops, NULL);
1713 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1715 g_ptr_array_free_full(ops);
1719 } else if (parent_class->recycle_file) {
1720 return parent_class->recycle_file(dself, filenum);
1727 static void finish_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1728 GenericOp * op = data;
1729 op->result = GINT_TO_POINTER(device_finish(op->child));
1733 rait_device_finish (Device * self) {
1737 ops = make_generic_boolean_op_array(RAIT_DEVICE(self));
1739 do_rait_child_ops(finish_do_op, ops, NULL);
1741 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1743 g_ptr_array_free_full(ops);
1747 } else if (parent_class->finish) {
1748 return parent_class->finish(self);
1755 rait_device_factory (char * type, char * name) {
1757 g_assert(0 == strcmp(type, "rait"));
1758 rval = DEVICE(g_object_new(TYPE_RAIT_DEVICE, NULL));
1759 if (!device_open_device(rval, name)) {
1760 g_object_unref(rval);
1767 Device * rait_device_new_from_devices(Device ** devices) {
1770 gboolean success = TRUE;
1772 g_return_val_if_fail(devices != NULL && *devices != NULL, NULL);
1774 rval = RAIT_DEVICE(g_object_new(TYPE_RAIT_DEVICE, NULL));
1776 for (i = 0; devices[i] != NULL; i ++) {
1777 g_assert(IS_DEVICE(devices[i]));
1778 if (devices[i]->access_mode != ACCESS_NULL) {
1782 g_object_ref(devices[i]);
1783 g_ptr_array_add(PRIVATE(rval)->children, devices[i]);
1786 success = success && find_block_size(rval);
1789 g_ptr_array_free(PRIVATE(rval)->children, TRUE);
1792 register_properties(rval);
1794 return DEVICE(rval);
1799 rait_device_register (void) {
1800 static const char * device_prefix_list[] = {"rait", NULL};
1801 register_device(rait_device_factory, device_prefix_list);