2 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
22 /* The RAIT device encapsulates some number of other devices into a single
23 * redundant device. */
29 #include "glib-util.h"
31 #include "fileheader.h"
32 #include "amsemaphore.h"
34 /* Just a note about the failure mode of different operations:
35 - Recovers from a failure (enters degraded mode)
37 seek_file() -- explodes if headers don't match.
38 seek_block() -- explodes if headers don't match.
39 read_block() -- explodes if data doesn't match.
41 - Operates in degraded mode (but dies if a new problem shows up)
42 read_label() -- but dies on label mismatch.
43 start() -- but dies when writing in degraded mode.
47 - Dies in degraded mode (even if remaining devices are OK)
55 * Type checking and casting macros
57 #define TYPE_RAIT_DEVICE (rait_device_get_type())
58 #define RAIT_DEVICE(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), rait_device_get_type(), RaitDevice)
59 #define RAIT_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), rait_device_get_type(), RaitDevice const)
60 #define RAIT_DEVICE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), rait_device_get_type(), RaitDeviceClass)
61 #define IS_RAIT_DEVICE(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), rait_device_get_type ())
63 #define RAIT_DEVICE_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), rait_device_get_type(), RaitDeviceClass)
64 static GType rait_device_get_type (void);
67 * Main object structure
69 typedef struct RaitDevice_s {
72 struct RaitDevicePrivate_s * private;
78 typedef struct _RaitDeviceClass RaitDeviceClass;
79 struct _RaitDeviceClass {
80 DeviceClass __parent__;
84 RAIT_STATUS_COMPLETE, /* All subdevices OK. */
85 RAIT_STATUS_DEGRADED, /* One subdevice failed. */
86 RAIT_STATUS_FAILED /* Two or more subdevices failed. */
89 /* Older versions of glib have a deadlock in their thread pool implementations,
90 * so we include a simple thread-pool implementation here to replace it.
92 * This implementation assumes that threads are used for paralellizing a single
93 * operation, so all threads run a function to completion before the main thread
94 * continues. This simplifies some of the locking semantics, and in particular
95 * there is no need to wait for stray threads to finish an operation when
96 * finalizing the RaitDevice object or when beginning a new operation.
98 #if !(GLIB_CHECK_VERSION(2,10,0))
99 #define USE_INTERNAL_THREADPOOL
102 typedef struct RaitDevicePrivate_s {
103 GPtrArray * children;
104 /* These flags are only relevant for reading. */
106 /* If status == RAIT_STATUS_DEGRADED, this holds the index of the
107 failed node. It holds a negative number otherwise. */
110 /* the child block size */
111 gsize child_block_size;
113 #ifdef USE_INTERNAL_THREADPOOL
114 /* array of ThreadInfo for performing parallel operations */
117 /* value of this semaphore is the number of threaded operations
119 amsemaphore_t *threads_sem;
123 #ifdef USE_INTERNAL_THREADPOOL
124 typedef struct ThreadInfo {
127 /* struct fields below are protected by this mutex and condition variable */
135 /* give threads access to active_threads and its mutex/cond */
136 struct RaitDevicePrivate_s *private;
140 /* This device uses a special sentinel node to indicate that the child devices
141 * will be set later (in rait_device_open). It contains a control character to
142 * make it difficult to enter accidentally in an Amanda config. */
143 #define DEFER_CHILDREN_SENTINEL "DEFER\1"
145 #define PRIVATE(o) (o->private)
147 #define rait_device_in_error(dev) \
148 (device_in_error((dev)) || PRIVATE(RAIT_DEVICE((dev)))->status == RAIT_STATUS_FAILED)
150 void rait_device_register (void);
152 /* here are local prototypes */
153 static void rait_device_init (RaitDevice * o);
154 static void rait_device_class_init (RaitDeviceClass * c);
155 static void rait_device_base_init (RaitDeviceClass * c);
156 static void rait_device_open_device (Device * self, char * device_name, char * device_type, char * device_node);
157 static gboolean rait_device_start (Device * self, DeviceAccessMode mode,
158 char * label, char * timestamp);
159 static gboolean rait_device_configure(Device * self, gboolean use_global_config);
160 static gboolean rait_device_start_file(Device * self, dumpfile_t * info);
161 static gboolean rait_device_write_block (Device * self, guint size, gpointer data);
162 static gboolean rait_device_finish_file (Device * self);
163 static dumpfile_t * rait_device_seek_file (Device * self, guint file);
164 static gboolean rait_device_seek_block (Device * self, guint64 block);
165 static int rait_device_read_block (Device * self, gpointer buf,
167 static gboolean rait_device_recycle_file (Device * self, guint filenum);
168 static gboolean rait_device_finish (Device * self);
169 static DeviceStatusFlags rait_device_read_label(Device * dself);
170 static void find_simple_params(RaitDevice * self, guint * num_children,
171 guint * data_children);
173 /* property handlers */
175 static gboolean property_get_block_size_fn(Device *self,
176 DevicePropertyBase *base, GValue *val,
177 PropertySurety *surety, PropertySource *source);
179 static gboolean property_set_block_size_fn(Device *self,
180 DevicePropertyBase *base, GValue *val,
181 PropertySurety surety, PropertySource source);
183 static gboolean property_get_canonical_name_fn(Device *self,
184 DevicePropertyBase *base, GValue *val,
185 PropertySurety *surety, PropertySource *source);
187 static gboolean property_get_concurrency_fn(Device *self,
188 DevicePropertyBase *base, GValue *val,
189 PropertySurety *surety, PropertySource *source);
191 static gboolean property_get_streaming_fn(Device *self,
192 DevicePropertyBase *base, GValue *val,
193 PropertySurety *surety, PropertySource *source);
195 static gboolean property_get_boolean_and_fn(Device *self,
196 DevicePropertyBase *base, GValue *val,
197 PropertySurety *surety, PropertySource *source);
199 static gboolean property_get_medium_access_type_fn(Device *self,
200 DevicePropertyBase *base, GValue *val,
201 PropertySurety *surety, PropertySource *source);
203 static gboolean property_get_max_volume_usage_fn(Device *self,
204 DevicePropertyBase *base, GValue *val,
205 PropertySurety *surety, PropertySource *source);
207 static gboolean property_set_max_volume_usage_fn(Device *self,
208 DevicePropertyBase *base, GValue *val,
209 PropertySurety surety, PropertySource source);
212 /* pointer to the class of our parent */
213 static DeviceClass *parent_class = NULL;
216 rait_device_get_type (void)
218 static GType type = 0;
220 if G_UNLIKELY(type == 0) {
221 static const GTypeInfo info = {
222 sizeof (RaitDeviceClass),
223 (GBaseInitFunc) rait_device_base_init,
224 (GBaseFinalizeFunc) NULL,
225 (GClassInitFunc) rait_device_class_init,
226 (GClassFinalizeFunc) NULL,
227 NULL /* class_data */,
230 (GInstanceInitFunc) rait_device_init,
234 type = g_type_register_static (TYPE_DEVICE, "RaitDevice", &info,
241 static void g_object_unref_foreach(gpointer data,
242 gpointer user_data G_GNUC_UNUSED) {
243 if (data != NULL && G_IS_OBJECT(data)) {
244 g_object_unref(data);
249 rait_device_finalize(GObject *obj_self)
251 RaitDevice *self = RAIT_DEVICE (obj_self);
252 if(G_OBJECT_CLASS(parent_class)->finalize) \
253 (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
254 if(self->private->children) {
255 g_ptr_array_foreach(self->private->children,
256 g_object_unref_foreach, NULL);
257 g_ptr_array_free (self->private->children, TRUE);
258 self->private->children = NULL;
260 #ifdef USE_INTERNAL_THREADPOOL
261 g_assert(PRIVATE(self)->threads_sem == NULL || PRIVATE(self)->threads_sem->value == 0);
263 if (PRIVATE(self)->threads) {
266 for (i = 0; i < PRIVATE(self)->threads->len; i++) {
267 ThreadInfo *inf = &g_array_index(PRIVATE(self)->threads, ThreadInfo, i);
269 /* NOTE: the thread is waiting on this condition right now, not
270 * executing an operation. */
272 /* ask the thread to die */
273 g_mutex_lock(inf->mutex);
275 g_cond_signal(inf->cond);
276 g_mutex_unlock(inf->mutex);
278 /* and wait for it to die, which should happen soon */
279 g_thread_join(inf->thread);
283 g_mutex_free(inf->mutex);
285 g_cond_free(inf->cond);
289 if (PRIVATE(self)->threads_sem)
290 amsemaphore_free(PRIVATE(self)->threads_sem);
292 amfree(self->private);
296 rait_device_init (RaitDevice * o G_GNUC_UNUSED)
298 PRIVATE(o) = g_new(RaitDevicePrivate, 1);
299 PRIVATE(o)->children = g_ptr_array_new();
300 PRIVATE(o)->status = RAIT_STATUS_COMPLETE;
301 PRIVATE(o)->failed = -1;
302 #ifdef USE_INTERNAL_THREADPOOL
303 PRIVATE(o)->threads = NULL;
304 PRIVATE(o)->threads_sem = NULL;
309 rait_device_class_init (RaitDeviceClass * c)
311 GObjectClass *g_object_class = (GObjectClass*) c;
312 DeviceClass *device_class = (DeviceClass *)c;
314 parent_class = g_type_class_ref (TYPE_DEVICE);
316 device_class->open_device = rait_device_open_device;
317 device_class->configure = rait_device_configure;
318 device_class->start = rait_device_start;
319 device_class->start_file = rait_device_start_file;
320 device_class->write_block = rait_device_write_block;
321 device_class->finish_file = rait_device_finish_file;
322 device_class->seek_file = rait_device_seek_file;
323 device_class->seek_block = rait_device_seek_block;
324 device_class->read_block = rait_device_read_block;
325 device_class->recycle_file = rait_device_recycle_file;
326 device_class->finish = rait_device_finish;
327 device_class->read_label = rait_device_read_label;
329 g_object_class->finalize = rait_device_finalize;
331 #ifndef USE_INTERNAL_THREADPOOL
332 #if !GLIB_CHECK_VERSION(2,10,2)
333 /* Versions of glib before 2.10.2 crash if
334 * g_thread_pool_set_max_unused_threads is called before the first
335 * invocation of g_thread_pool_new. So we make up a thread pool, but don't
336 * start any threads in it, and free it */
338 GThreadPool *pool = g_thread_pool_new((GFunc)-1, NULL, -1, FALSE, NULL);
339 g_thread_pool_free(pool, TRUE, FALSE);
343 g_thread_pool_set_max_unused_threads(-1);
348 rait_device_base_init (RaitDeviceClass * c)
350 DeviceClass *device_class = (DeviceClass *)c;
352 /* the RAIT device overrides most of the standard properties, so that it
353 * can calculate them by querying the same property on the children */
354 device_class_register_property(device_class, PROPERTY_BLOCK_SIZE,
355 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
356 property_get_block_size_fn,
357 property_set_block_size_fn);
359 device_class_register_property(device_class, PROPERTY_CANONICAL_NAME,
360 PROPERTY_ACCESS_GET_MASK,
361 property_get_canonical_name_fn, NULL);
363 device_class_register_property(device_class, PROPERTY_CONCURRENCY,
364 PROPERTY_ACCESS_GET_MASK,
365 property_get_concurrency_fn, NULL);
367 device_class_register_property(device_class, PROPERTY_STREAMING,
368 PROPERTY_ACCESS_GET_MASK,
369 property_get_streaming_fn, NULL);
371 device_class_register_property(device_class, PROPERTY_APPENDABLE,
372 PROPERTY_ACCESS_GET_MASK,
373 property_get_boolean_and_fn, NULL);
375 device_class_register_property(device_class, PROPERTY_PARTIAL_DELETION,
376 PROPERTY_ACCESS_GET_MASK,
377 property_get_boolean_and_fn, NULL);
379 device_class_register_property(device_class, PROPERTY_FULL_DELETION,
380 PROPERTY_ACCESS_GET_MASK,
381 property_get_boolean_and_fn, NULL);
383 device_class_register_property(device_class, PROPERTY_LEOM,
384 PROPERTY_ACCESS_GET_MASK,
385 property_get_boolean_and_fn, NULL);
387 device_class_register_property(device_class, PROPERTY_MEDIUM_ACCESS_TYPE,
388 PROPERTY_ACCESS_GET_MASK,
389 property_get_medium_access_type_fn, NULL);
391 device_class_register_property(device_class, PROPERTY_MAX_VOLUME_USAGE,
392 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
393 property_get_max_volume_usage_fn,
394 property_set_max_volume_usage_fn);
397 /* This function does something a little clever and a little
398 * complicated. It takes an array of operations and runs the given
399 * function on each element in the array. The trick is that it runs them
400 * all in parallel, in different threads. This is more efficient than it
401 * sounds because we use a GThreadPool, which means calling this function
402 * will probably not start any new threads at all, but rather use
403 * existing ones. The func is called with two gpointer arguments: The
404 * first from the array, the second is the data argument.
406 * When it returns, all the operations have been successfully
407 * executed. If you want results from your operations, do it yourself
411 #ifdef USE_INTERNAL_THREADPOOL
412 static gpointer rait_thread_pool_func(gpointer data) {
413 ThreadInfo *inf = data;
415 g_mutex_lock(inf->mutex);
417 while (!inf->die && !inf->func)
418 g_cond_wait(inf->cond, inf->mutex);
424 /* invoke the function */
425 inf->func(inf->data, NULL);
429 /* indicate that we're finished; will not block */
430 amsemaphore_down(inf->private->threads_sem);
433 g_mutex_unlock(inf->mutex);
437 static void do_thread_pool_op(RaitDevice *self, GFunc func, GPtrArray * ops) {
440 if (PRIVATE(self)->threads_sem == NULL)
441 PRIVATE(self)->threads_sem = amsemaphore_new_with_value(0);
443 if (PRIVATE(self)->threads == NULL)
444 PRIVATE(self)->threads = g_array_sized_new(FALSE, TRUE,
445 sizeof(ThreadInfo), ops->len);
447 g_assert(PRIVATE(self)->threads_sem->value == 0);
449 if (PRIVATE(self)->threads->len < ops->len)
450 g_array_set_size(PRIVATE(self)->threads, ops->len);
452 /* the semaphore will hit zero when each thread has decremented it */
453 amsemaphore_force_set(PRIVATE(self)->threads_sem, ops->len);
455 for (i = 0; i < ops->len; i++) {
456 ThreadInfo *inf = &g_array_index(PRIVATE(self)->threads, ThreadInfo, i);
458 inf->mutex = g_mutex_new();
459 inf->cond = g_cond_new();
460 inf->private = PRIVATE(self);
461 inf->thread = g_thread_create(rait_thread_pool_func, inf, TRUE, NULL);
464 /* set up the info the thread needs and trigger it to start */
465 g_mutex_lock(inf->mutex);
466 inf->data = g_ptr_array_index(ops, i);
468 g_cond_signal(inf->cond);
469 g_mutex_unlock(inf->mutex);
472 /* wait until semaphore hits zero */
473 amsemaphore_wait_empty(PRIVATE(self)->threads_sem);
476 #else /* USE_INTERNAL_THREADPOOL */
478 static void do_thread_pool_op(RaitDevice *self G_GNUC_UNUSED, GFunc func, GPtrArray * ops) {
482 pool = g_thread_pool_new(func, NULL, -1, FALSE, NULL);
483 for (i = 0; i < ops->len; i ++) {
484 g_thread_pool_push(pool, g_ptr_array_index(ops, i), NULL);
487 g_thread_pool_free(pool, FALSE, TRUE);
490 #endif /* USE_INTERNAL_THREADPOOL */
492 /* This does the above, in a serial fashion (and without using threads) */
493 static void do_unthreaded_ops(RaitDevice *self G_GNUC_UNUSED, GFunc func, GPtrArray * ops) {
496 for (i = 0; i < ops->len; i ++) {
497 func(g_ptr_array_index(ops, i), NULL);
501 /* This is the one that code below should call. It switches
502 automatically between do_thread_pool_op and do_unthreaded_ops,
503 depending on g_thread_supported(). */
504 static void do_rait_child_ops(RaitDevice *self, GFunc func, GPtrArray * ops) {
505 if (g_thread_supported()) {
506 do_thread_pool_op(self, func, ops);
508 do_unthreaded_ops(self, func, ops);
513 child_device_names_to_rait_name(RaitDevice * self) {
515 char *braced, *result;
518 kids = g_ptr_array_sized_new(self->private->children->len);
519 for (i = 0; i < self->private->children->len; i ++) {
520 Device *child = g_ptr_array_index(self->private->children, i);
521 const char *child_name = NULL;
523 gboolean got_prop = FALSE;
525 bzero(&val, sizeof(val));
527 if ((signed)i != self->private->failed) {
528 if (device_property_get(child, PROPERTY_CANONICAL_NAME, &val)) {
529 child_name = g_value_get_string(&val);
535 child_name = "MISSING";
537 g_ptr_array_add(kids, g_strdup(child_name));
543 braced = collapse_braced_alternates(kids);
544 result = g_strdup_printf("rait:%s", braced);
550 /* Find a workable child block size, based on the block size ranges of our
553 * The algorithm is to construct the intersection of all child devices'
554 * [min,max] block size ranges, and then pick the block size closest to 32k
555 * that is in the resulting range. This avoids picking ridiculously small (1
556 * byte) or large (INT_MAX) block sizes when using devices with wide-open block
559 * This function returns the calculated child block size directly, and the RAIT
560 * device's blocksize via rait_size, if not NULL. It is resilient to errors in
561 * a single child device, but sets the device's error status and returns 0 if
562 * it cannot determine an agreeable block size.
565 calculate_block_size_from_children(RaitDevice * self, gsize *rait_size)
568 gsize max = SIZE_MAX;
569 gboolean found_one = FALSE;
573 for (i = 0; i < self->private->children->len; i ++) {
574 gsize child_min = SIZE_MAX, child_max = 0;
576 GValue property_result;
577 PropertySource source;
579 bzero(&property_result, sizeof(property_result));
581 if ((signed)i == self->private->failed)
584 child = g_ptr_array_index(self->private->children, i);
585 if (!device_property_get_ex(child, PROPERTY_BLOCK_SIZE,
586 &property_result, NULL, &source)) {
587 g_warning("Error getting BLOCK_SIZE from %s: %s",
588 child->device_name, device_error_or_status(child));
592 /* if the block size has been set explicitly, then we need to use that blocksize;
593 * otherwise (even if it was DETECTED), override it. */
594 if (source == PROPERTY_SOURCE_USER) {
595 child_min = child_max = g_value_get_int(&property_result);
597 if (!device_property_get(child, PROPERTY_MIN_BLOCK_SIZE,
599 g_warning("Error getting MIN_BLOCK_SIZE from %s: %s",
600 child->device_name, device_error_or_status(child));
603 child_min = g_value_get_uint(&property_result);
605 if (!device_property_get(child, PROPERTY_MAX_BLOCK_SIZE,
607 g_warning("Error getting MAX_BLOCK_SIZE from %s: %s",
608 child->device_name, device_error_or_status(child));
611 child_max = g_value_get_uint(&property_result);
613 if (child_min == 0 || child_max == 0 || (child_min > child_max)) {
614 g_warning("Invalid min, max block sizes from %s", child->device_name);
620 min = MAX(min, child_min);
621 max = MIN(max, child_max);
625 device_set_error((Device*)self,
626 stralloc(_("Could not find any child devices' block size ranges")),
627 DEVICE_STATUS_DEVICE_ERROR);
632 device_set_error((Device*)self,
633 stralloc(_("No block size is acceptable to all child devices")),
634 DEVICE_STATUS_DEVICE_ERROR);
638 /* Now pick a number. If 32k is in range, we use that; otherwise, we use
639 * the nearest acceptable size. */
640 result = CLAMP(32768, min, max);
644 find_simple_params(self, NULL, &data_children);
645 *rait_size = result * data_children;
651 /* Set BLOCK_SIZE on all children */
653 set_block_size_on_children(RaitDevice *self, gsize child_block_size)
657 PropertySource source;
659 bzero(&val, sizeof(val));
661 g_assert(child_block_size < INT_MAX);
662 g_value_init(&val, G_TYPE_INT);
663 g_value_set_int(&val, (gint)child_block_size);
665 for (i = 0; i < self->private->children->len; i ++) {
667 GValue property_result;
669 bzero(&property_result, sizeof(property_result));
671 if ((signed)i == self->private->failed)
674 child = g_ptr_array_index(self->private->children, i);
676 /* first, make sure the block size is at its default, or is already
678 if (device_property_get_ex(child, PROPERTY_BLOCK_SIZE,
679 &property_result, NULL, &source)) {
680 gsize from_child = g_value_get_int(&property_result);
681 g_value_unset(&property_result);
682 if (source != PROPERTY_SOURCE_DEFAULT
683 && from_child != child_block_size) {
684 device_set_error((Device *)self,
685 vstrallocf(_("Child device %s already has its block size set to %zd, not %zd"),
686 child->device_name, from_child, child_block_size),
687 DEVICE_STATUS_DEVICE_ERROR);
691 /* failing to get the block size isn't necessarily fatal.. */
692 g_warning("Error getting BLOCK_SIZE from %s: %s",
693 child->device_name, device_error_or_status(child));
696 if (!device_property_set(child, PROPERTY_BLOCK_SIZE, &val)) {
697 device_set_error((Device *)self,
698 vstrallocf(_("Error setting block size on %s"), child->device_name),
699 DEVICE_STATUS_DEVICE_ERROR);
707 /* The time for users to specify block sizes has ended; set this device's
708 * block-size attributes for easy access by other RAIT functions. Returns
709 * FALSE on error, with the device's error status already set. */
711 fix_block_size(RaitDevice *self)
713 Device *dself = (Device *)self;
714 gsize my_block_size, child_block_size;
716 if (dself->block_size_source == PROPERTY_SOURCE_DEFAULT) {
717 child_block_size = calculate_block_size_from_children(self, &my_block_size);
718 if (child_block_size == 0)
721 self->private->child_block_size = child_block_size;
722 dself->block_size = my_block_size;
723 dself->block_size_surety = PROPERTY_SURETY_GOOD;
724 dself->block_size_source = PROPERTY_SOURCE_DETECTED;
728 find_simple_params(self, NULL, &data_children);
729 g_assert((dself->block_size % data_children) == 0);
730 child_block_size = dself->block_size / data_children;
733 /* now tell the children we mean it */
734 if (!set_block_size_on_children(self, child_block_size))
740 /* This structure contains common fields for many operations. Not all
741 operations use all fields, however. */
743 gpointer result; /* May be a pointer; may be an integer or boolean
744 stored with GINT_TO_POINTER. */
745 Device * child; /* The device in question. Used by all
747 guint child_index; /* For recoverable operations (read-related
748 operations), this field provides the number
749 of this child in the self->private->children
753 typedef gboolean (*BooleanExtractor)(gpointer data);
755 /* A BooleanExtractor */
756 static gboolean extract_boolean_generic_op(gpointer data) {
757 GenericOp * op = data;
758 return GPOINTER_TO_INT(op->result);
761 /* A BooleanExtractor */
762 static gboolean extract_boolean_pointer_op(gpointer data) {
763 GenericOp * op = data;
764 return op->result != NULL;
767 /* Does the equivalent of this perl command:
768 ! (first { !extractor($_) } @_
769 That is, calls extractor on each element of the array, and returns
770 TRUE if and only if all calls to extractor return TRUE. This function
771 stops as soon as an extractor returns false, so it's best if extractor
772 functions have no side effects.
774 static gboolean g_ptr_array_and(GPtrArray * array,
775 BooleanExtractor extractor) {
777 if (array == NULL || array->len <= 0)
780 for (i = 0; i < array->len; i ++) {
781 if (!extractor(g_ptr_array_index(array, i)))
788 /* Takes a RaitDevice, and makes a GPtrArray of GenericOp. */
789 static GPtrArray * make_generic_boolean_op_array(RaitDevice* self) {
793 rval = g_ptr_array_sized_new(self->private->children->len);
794 for (i = 0; i < self->private->children->len; i ++) {
797 if ((signed)i == self->private->failed) {
801 op = g_new(GenericOp, 1);
802 op->child = g_ptr_array_index(self->private->children, i);
804 g_ptr_array_add(rval, op);
810 /* Takes a GPtrArray of GenericOp, and a BooleanExtractor, and does
811 all the proper handling for the result of operations that allow
812 device isolation. Returns FALSE only if an unrecoverable error
814 static gboolean g_ptr_array_union_robust(RaitDevice * self, GPtrArray * ops,
815 BooleanExtractor extractor) {
820 /* We found one or more failed elements. See which elements failed, and
822 for (i = 0; i < ops->len; i ++) {
823 GenericOp * op = g_ptr_array_index(ops, i);
824 if (!extractor(op)) {
825 self->private->failed = op->child_index;
826 g_warning("RAIT array %s isolated device %s: %s",
827 DEVICE(self)->device_name,
828 op->child->device_name,
829 device_error(op->child));
835 /* no failures? great! */
839 /* a single failure in COMPLETE just puts us in DEGRADED mode */
840 if (self->private->status == RAIT_STATUS_COMPLETE && nfailed == 1) {
841 self->private->status = RAIT_STATUS_DEGRADED;
842 self->private->failed = lastfailed;
843 g_warning("RAIT array %s DEGRADED", DEVICE(self)->device_name);
846 self->private->status = RAIT_STATUS_FAILED;
847 g_warning("RAIT array %s FAILED", DEVICE(self)->device_name);
855 char * device_name; /* IN */
856 Device * result; /* OUT */
860 static void device_open_do_op(gpointer data,
861 gpointer user_data G_GNUC_UNUSED) {
862 OpenDeviceOp * op = data;
864 if (strcmp(op->device_name, "ERROR") == 0 ||
865 strcmp(op->device_name, "MISSING") == 0 ||
866 strcmp(op->device_name, "DEGRADED") == 0) {
867 g_warning("RAIT device %s contains a missing element, attempting "
868 "degraded mode.\n", op->rait_name);
871 op->result = device_open(op->device_name);
875 /* Returns TRUE if and only if the volume label and time are equal. */
876 static gboolean compare_volume_results(Device * a, Device * b) {
877 return (0 == compare_possibly_null_strings(a->volume_time, b->volume_time)
878 && 0 == compare_possibly_null_strings(a->volume_label, b->volume_label));
881 /* Stickes new_message at the end of *old_message; frees new_message and
882 * may change *old_message. */
883 static void append_message(char ** old_message, char * new_message) {
885 if (*old_message == NULL || **old_message == '\0') {
888 rval = g_strdup_printf("%s; %s", *old_message, new_message);
891 amfree(*old_message);
896 open_child_devices (Device * dself, char * device_name,
897 char * device_node) {
898 GPtrArray *device_names;
899 GPtrArray * device_open_ops;
902 char *failure_errmsgs;
903 DeviceStatusFlags failure_flags;
906 self = RAIT_DEVICE(dself);
908 device_names = expand_braced_alternates(device_node);
910 if (device_names == NULL) {
911 device_set_error(dself,
912 vstrallocf(_("Invalid RAIT device name '%s'"), device_name),
913 DEVICE_STATUS_DEVICE_ERROR);
917 /* Open devices in a separate thread, in case they have to rewind etc. */
918 device_open_ops = g_ptr_array_new();
920 for (i = 0; i < device_names->len; i++) {
922 char *name = g_ptr_array_index(device_names, i);
924 op = g_new(OpenDeviceOp, 1);
925 op->device_name = name;
928 op->rait_name = device_name;
929 g_ptr_array_add(device_open_ops, op);
932 g_ptr_array_free(device_names, TRUE);
933 do_rait_child_ops(self, device_open_do_op, device_open_ops);
936 failure_errmsgs = NULL;
939 /* Check results of opening devices. */
940 for (i = 0; i < device_open_ops->len; i ++) {
941 OpenDeviceOp *op = g_ptr_array_index(device_open_ops, i);
943 if (op->result != NULL &&
944 op->result->status == DEVICE_STATUS_SUCCESS) {
945 g_ptr_array_add(self->private->children, op->result);
947 char * this_failure_errmsg =
948 g_strdup_printf("%s: %s", op->device_name,
949 device_error_or_status(op->result));
950 DeviceStatusFlags status =
952 DEVICE_STATUS_DEVICE_ERROR : op->result->status;
953 append_message(&failure_errmsgs,
954 strdup(this_failure_errmsg));
955 failure_flags |= status;
956 if (self->private->status == RAIT_STATUS_COMPLETE) {
957 /* The first failure just puts us in degraded mode. */
959 device_name, this_failure_errmsg);
960 g_warning("%s: %s failed, entering degraded mode.",
961 device_name, op->device_name);
962 g_ptr_array_add(self->private->children, op->result);
963 self->private->status = RAIT_STATUS_DEGRADED;
964 self->private->failed = i;
966 /* The second and further failures are fatal. */
970 amfree(op->device_name);
973 g_ptr_array_free_full(device_open_ops);
976 self->private->status = RAIT_STATUS_FAILED;
977 device_set_error(dself, failure_errmsgs, failure_flags);
985 rait_device_open_device (Device * dself, char * device_name,
986 char * device_type G_GNUC_UNUSED, char * device_node) {
988 if (0 != strcmp(device_node, DEFER_CHILDREN_SENTINEL)) {
989 if (!open_child_devices(dself, device_name, device_node))
993 if (parent_class->open_device) {
994 parent_class->open_device(dself, device_name, device_type, device_node);
1000 rait_device_open_from_children (GSList *child_devices) {
1008 /* first, open a RAIT device using the DEFER_CHILDREN_SENTINEL */
1009 dself = device_open("rait:" DEFER_CHILDREN_SENTINEL);
1010 if (!IS_RAIT_DEVICE(dself)) {
1014 /* set its children */
1015 self = RAIT_DEVICE(dself);
1017 for (i=0, iter = child_devices; iter; i++, iter = iter->next) {
1018 Device *kid = iter->data;
1020 /* a NULL kid is OK -- it opens the device in degraded mode */
1023 self->private->failed = i;
1025 g_assert(IS_DEVICE(kid));
1026 g_object_ref((GObject *)kid);
1029 g_ptr_array_add(self->private->children, kid);
1032 /* and set the status based on the children */
1033 switch (nfailures) {
1035 self->private->status = RAIT_STATUS_COMPLETE;
1039 self->private->status = RAIT_STATUS_DEGRADED;
1043 self->private->status = RAIT_STATUS_FAILED;
1044 device_set_error(dself,
1045 _("more than one child device is missing"),
1046 DEVICE_STATUS_DEVICE_ERROR);
1050 /* create a name from the children's names and use it to chain up
1051 * to open_device (we skipped this step in rait_device_open_device) */
1052 device_name = child_device_names_to_rait_name(self);
1054 if (parent_class->open_device) {
1055 parent_class->open_device(dself,
1056 device_name, "rait",
1057 device_name+5); /* (+5 skips "rait:") */
1064 static void read_label_do_op(gpointer data,
1065 gpointer user_data G_GNUC_UNUSED) {
1066 GenericOp * op = data;
1067 op->result = GINT_TO_POINTER(device_read_label(op->child));
1070 static DeviceStatusFlags rait_device_read_label(Device * dself) {
1073 DeviceStatusFlags failed_result = 0;
1074 char *failed_errmsg = NULL;
1076 Device * first_success = NULL;
1078 self = RAIT_DEVICE(dself);
1080 amfree(dself->volume_time);
1081 amfree(dself->volume_label);
1082 dumpfile_free(dself->volume_header);
1083 dself->volume_header = NULL;
1085 if (rait_device_in_error(self))
1086 return dself->status | DEVICE_STATUS_DEVICE_ERROR;
1088 /* nail down our block size, if we haven't already */
1089 if (!fix_block_size(self))
1092 ops = make_generic_boolean_op_array(self);
1094 do_rait_child_ops(self, read_label_do_op, ops);
1096 for (i = 0; i < ops->len; i ++) {
1097 GenericOp * op = g_ptr_array_index(ops, i);
1098 DeviceStatusFlags result = GPOINTER_TO_INT(op->result);
1099 if (op->result == DEVICE_STATUS_SUCCESS) {
1100 if (first_success == NULL) {
1101 /* This is the first successful device. */
1102 first_success = op->child;
1103 } else if (!compare_volume_results(first_success, op->child)) {
1104 /* Doesn't match. :-( */
1105 failed_errmsg = vstrallocf("Inconsistent volume labels/datestamps: "
1106 "Got %s/%s on %s against %s/%s on %s.",
1107 first_success->volume_label,
1108 first_success->volume_time,
1109 first_success->device_name,
1110 op->child->volume_label,
1111 op->child->volume_time,
1112 op->child->device_name);
1113 g_warning("%s", failed_errmsg);
1114 failed_result |= DEVICE_STATUS_VOLUME_ERROR;
1117 failed_result |= result;
1121 if (failed_result != DEVICE_STATUS_SUCCESS) {
1122 /* We had multiple failures or an inconsistency. */
1123 device_set_error(dself, failed_errmsg, failed_result);
1125 /* Everything peachy. */
1126 amfree(failed_errmsg);
1128 g_assert(first_success != NULL);
1129 if (first_success->volume_label != NULL) {
1130 dself->volume_label = g_strdup(first_success->volume_label);
1132 if (first_success->volume_time != NULL) {
1133 dself->volume_time = g_strdup(first_success->volume_time);
1135 if (first_success->volume_header != NULL) {
1136 dself->volume_header = dumpfile_copy(first_success->volume_header);
1138 dself->header_block_size = first_success->header_block_size;
1141 g_ptr_array_free_full(ops);
1143 return dself->status;
1148 DeviceAccessMode mode; /* IN */
1149 char * label; /* IN */
1150 char * timestamp; /* IN */
1154 static void start_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1156 StartOp * param = data;
1158 klass = DEVICE_GET_CLASS(param->base.child);
1160 param->base.result =
1161 GINT_TO_POINTER((klass->start)(param->base.child,
1162 param->mode, param->label,
1165 param->base.result = FALSE;
1170 rait_device_configure(Device * dself, gboolean use_global_config)
1172 RaitDevice *self = RAIT_DEVICE(dself);
1175 for (i = 0; i < self->private->children->len; i ++) {
1178 if ((signed)i == self->private->failed)
1181 child = g_ptr_array_index(self->private->children, i);
1182 /* unconditionally configure the child without the global
1184 if (!device_configure(child, FALSE))
1188 if (parent_class->configure) {
1189 return parent_class->configure(dself, use_global_config);
1196 rait_device_start (Device * dself, DeviceAccessMode mode, char * label,
1202 DeviceStatusFlags total_status;
1203 char *failure_errmsgs = NULL;
1204 char * label_from_device = NULL;
1206 self = RAIT_DEVICE(dself);
1208 if (rait_device_in_error(self)) return FALSE;
1210 /* No starting in degraded mode. */
1211 if (self->private->status != RAIT_STATUS_COMPLETE &&
1212 (mode == ACCESS_WRITE || mode == ACCESS_APPEND)) {
1213 device_set_error(dself,
1214 g_strdup_printf(_("RAIT device %s is read-only "
1215 "because it is in degraded mode.\n"),
1216 dself->device_name),
1217 DEVICE_STATUS_DEVICE_ERROR);
1221 /* nail down our block size, if we haven't already */
1222 if (!fix_block_size(self))
1225 dself->access_mode = mode;
1226 g_mutex_lock(dself->device_mutex);
1227 dself->in_file = FALSE;
1228 g_mutex_unlock(dself->device_mutex);
1229 amfree(dself->volume_label);
1230 amfree(dself->volume_time);
1231 dumpfile_free(dself->volume_header);
1232 dself->volume_header = NULL;
1234 ops = g_ptr_array_sized_new(self->private->children->len);
1235 for (i = 0; i < self->private->children->len; i ++) {
1238 if ((signed)i == self->private->failed) {
1242 op = g_new(StartOp, 1);
1243 op->base.child = g_ptr_array_index(self->private->children, i);
1245 op->label = g_strdup(label);
1246 op->timestamp = g_strdup(timestamp);
1247 g_ptr_array_add(ops, op);
1250 do_rait_child_ops(self, start_do_op, ops);
1252 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1254 /* Check results of starting devices; this is mostly about the
1255 * VOLUME_UNLABELED flag. */
1257 for (i = 0; i < ops->len; i ++) {
1258 StartOp * op = g_ptr_array_index(ops, i);
1259 Device *child = op->base.child;
1261 total_status |= child->status;
1262 if (child->status != DEVICE_STATUS_SUCCESS) {
1263 /* record the error message and move on. */
1264 append_message(&failure_errmsgs,
1265 g_strdup_printf("%s: %s",
1267 device_error_or_status(child)));
1269 if (child->volume_label != NULL && child->volume_time != NULL) {
1270 if (label_from_device) {
1271 if (strcmp(child->volume_label, dself->volume_label) != 0 ||
1272 strcmp(child->volume_time, dself->volume_time) != 0) {
1273 /* Mismatch! (Two devices provided different labels) */
1274 char * this_message =
1275 g_strdup_printf("%s: Label (%s/%s) is different "
1276 "from label (%s/%s) found at "
1279 child->volume_label,
1281 dself->volume_label,
1284 append_message(&failure_errmsgs, this_message);
1285 total_status |= DEVICE_STATUS_DEVICE_ERROR;
1286 g_warning("RAIT device children have different labels or timestamps");
1289 /* First device with a volume. */
1290 dself->volume_label = g_strdup(child->volume_label);
1291 dself->volume_time = g_strdup(child->volume_time);
1292 dself->volume_header = dumpfile_copy(child->volume_header);
1293 label_from_device = g_strdup(child->device_name);
1296 /* Device problem, it says it succeeded but sets no label? */
1297 char * this_message =
1298 g_strdup_printf("%s: Says label read, but no volume "
1299 "label found.", child->device_name);
1300 g_warning("RAIT device child has NULL volume or label");
1301 append_message(&failure_errmsgs, this_message);
1302 total_status |= DEVICE_STATUS_DEVICE_ERROR;
1307 if (total_status == DEVICE_STATUS_SUCCESS) {
1308 StartOp * op = g_ptr_array_index(ops, 0);
1309 Device *child = op->base.child;
1310 dself->header_block_size = child->header_block_size;
1313 amfree(label_from_device);
1314 g_ptr_array_free_full(ops);
1316 dself->status = total_status;
1318 if (total_status != DEVICE_STATUS_SUCCESS || !success) {
1319 device_set_error(dself, failure_errmsgs, total_status);
1322 amfree(failure_errmsgs);
1328 dumpfile_t * info; /* IN */
1333 static void start_file_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1334 StartFileOp * op = data;
1335 op->base.result = GINT_TO_POINTER(device_start_file(op->base.child,
1337 op->fileno = op->base.child->file;
1338 if (op->fileno < 1) {
1339 op->base.result = FALSE;
1344 rait_device_start_file (Device * dself, dumpfile_t * info) {
1349 int actual_file = -1;
1351 self = RAIT_DEVICE(dself);
1353 if (rait_device_in_error(self)) return FALSE;
1354 if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1356 ops = g_ptr_array_sized_new(self->private->children->len);
1357 for (i = 0; i < self->private->children->len; i ++) {
1359 op = g_new(StartFileOp, 1);
1360 op->base.child = g_ptr_array_index(self->private->children, i);
1361 /* each child gets its own copy of the header, to munge as it
1362 * likes (setting blocksize, at least) */
1363 op->info = dumpfile_copy(info);
1364 g_ptr_array_add(ops, op);
1367 do_rait_child_ops(self, start_file_do_op, ops);
1369 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1371 for (i = 0; i < self->private->children->len && success; i ++) {
1372 StartFileOp * op = g_ptr_array_index(ops, i);
1373 if (!op->base.result)
1375 g_assert(op->fileno >= 1);
1376 if (actual_file < 1) {
1377 actual_file = op->fileno;
1379 if (actual_file != op->fileno) {
1380 /* File number mismatch! Aah, my hair is on fire! */
1381 device_set_error(dself,
1382 g_strdup_printf("File number mismatch in "
1383 "rait_device_start_file(): "
1384 "Child %s reported file number "
1385 "%d, another child reported "
1387 op->base.child->device_name,
1388 op->fileno, actual_file),
1389 DEVICE_STATUS_DEVICE_ERROR);
1391 op->base.result = FALSE;
1395 for (i = 0; i < ops->len && success; i ++) {
1396 StartFileOp * op = g_ptr_array_index(ops, i);
1397 if (op->info) dumpfile_free(op->info);
1399 g_ptr_array_free_full(ops);
1402 if (!device_in_error(dself)) {
1403 device_set_error(dself, stralloc("One or more devices "
1404 "failed to start_file"),
1405 DEVICE_STATUS_DEVICE_ERROR);
1410 g_assert(actual_file >= 1);
1411 dself->file = actual_file;
1412 g_mutex_lock(dself->device_mutex);
1413 dself->in_file = TRUE;
1414 dself->bytes_written = 0;
1415 g_mutex_unlock(dself->device_mutex);
1420 static void find_simple_params(RaitDevice * self,
1421 guint * num_children,
1422 guint * data_children) {
1425 num = self->private->children->len;
1430 if (num_children != NULL)
1431 *num_children = num;
1432 if (data_children != NULL)
1433 *data_children = data;
1438 guint size; /* IN */
1439 gpointer data; /* IN */
1440 gboolean data_needs_free; /* bookkeeping */
1444 static void write_block_do_op(gpointer data,
1445 gpointer user_data G_GNUC_UNUSED) {
1446 WriteBlockOp * op = data;
1449 GINT_TO_POINTER(device_write_block(op->base.child, op->size, op->data));
1452 /* Parity block generation. Performance of this function can be improved
1453 considerably by using larger-sized integers or
1454 assembly-coded vector instructions. Parameters are:
1455 % data - All data chunks in series (chunk_size * num_chunks bytes)
1456 % parity - Allocated space for parity block (chunk_size bytes)
1458 static void make_parity_block(char * data, char * parity,
1459 guint chunk_size, guint num_chunks) {
1461 bzero(parity, chunk_size);
1462 for (i = 0; i < num_chunks - 1; i ++) {
1464 for (j = 0; j < chunk_size; j ++) {
1465 parity[j] ^= data[chunk_size*i + j];
1470 /* Does the same thing as make_parity_block, but instead of using a
1471 single memory chunk holding all chunks, it takes a GPtrArray of
1473 static void make_parity_block_extents(GPtrArray * data, char * parity,
1476 bzero(parity, chunk_size);
1477 for (i = 0; i < data->len; i ++) {
1480 data_chunk = g_ptr_array_index(data, i);
1481 for (j = 0; j < chunk_size; j ++) {
1482 parity[j] ^= data_chunk[j];
1487 /* Does the parity creation algorithm. Allocates and returns a single
1488 device block from a larger RAIT block. chunks and chunk are 1-indexed. */
1489 static char * extract_data_block(char * data, guint size,
1490 guint chunks, guint chunk) {
1494 g_assert(chunks > 0 && chunk > 0 && chunk <= chunks);
1495 g_assert(data != NULL);
1496 g_assert(size > 0 && size % (chunks - 1) == 0);
1498 chunk_size = size / (chunks - 1);
1499 rval = g_malloc(chunk_size);
1500 if (chunks != chunk) {
1502 memcpy(rval, data + chunk_size * (chunk - 1), chunk_size);
1504 make_parity_block(data, rval, chunk_size, chunks);
1511 rait_device_write_block (Device * dself, guint size, gpointer data) {
1515 guint data_children, num_children;
1516 gsize blocksize = dself->block_size;
1518 gboolean last_block = (size < blocksize);
1520 self = RAIT_DEVICE(dself);
1522 if (rait_device_in_error(self)) return FALSE;
1523 if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1525 find_simple_params(RAIT_DEVICE(self), &num_children, &data_children);
1526 num_children = self->private->children->len;
1527 if (num_children != 1)
1528 data_children = num_children - 1;
1530 data_children = num_children;
1532 g_assert(size % data_children == 0 || last_block);
1534 /* zero out to the end of a short block -- tape devices only write
1539 new_data = g_malloc(blocksize);
1540 memcpy(new_data, data, size);
1541 bzero(new_data + size, blocksize - size);
1547 ops = g_ptr_array_sized_new(num_children);
1548 for (i = 0; i < self->private->children->len; i ++) {
1550 op = g_malloc(sizeof(*op));
1551 op->base.child = g_ptr_array_index(self->private->children, i);
1552 op->size = size / data_children;
1553 if (num_children <= 2) {
1555 op->data_needs_free = FALSE;
1557 op->data_needs_free = TRUE;
1558 op->data = extract_data_block(data, size, num_children, i + 1);
1560 g_ptr_array_add(ops, op);
1563 do_rait_child_ops(self, write_block_do_op, ops);
1565 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1567 for (i = 0; i < self->private->children->len; i ++) {
1568 WriteBlockOp * op = g_ptr_array_index(ops, i);
1569 if (op->data_needs_free)
1577 g_ptr_array_free_full(ops);
1580 /* TODO be more specific here */
1581 /* TODO: handle EOM here -- if one or more (or two or more??)
1582 * children have is_eom set, then reflect that in our error
1583 * status. What's more fun is when one device fails and must be isolated at
1584 * the same time another hits EOF. */
1585 device_set_error(dself,
1586 stralloc("One or more devices failed to write_block"),
1587 DEVICE_STATUS_DEVICE_ERROR);
1588 /* this is EOM or an error, so call it EOM */
1589 dself->is_eom = TRUE;
1593 g_mutex_lock(dself->device_mutex);
1594 dself->bytes_written += size;
1595 g_mutex_unlock(dself->device_mutex);
1602 static void finish_file_do_op(gpointer data,
1603 gpointer user_data G_GNUC_UNUSED) {
1604 GenericOp * op = data;
1606 op->result = GINT_TO_POINTER(device_finish_file(op->child));
1613 rait_device_finish_file (Device * dself) {
1616 RaitDevice * self = RAIT_DEVICE(dself);
1618 g_assert(self != NULL);
1619 if (rait_device_in_error(dself)) return FALSE;
1620 if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1622 ops = make_generic_boolean_op_array(self);
1624 do_rait_child_ops(self, finish_file_do_op, ops);
1626 success = g_ptr_array_and(ops, extract_boolean_generic_op);
1628 g_ptr_array_free_full(ops);
1631 /* TODO: be more specific here */
1632 device_set_error(dself,
1633 g_strdup("One or more devices failed to finish_file"),
1634 DEVICE_STATUS_DEVICE_ERROR);
1638 g_mutex_lock(dself->device_mutex);
1639 dself->in_file = FALSE;
1640 g_mutex_unlock(dself->device_mutex);
1646 guint requested_file; /* IN */
1647 guint actual_file; /* OUT */
1651 static void seek_file_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1652 SeekFileOp * op = data;
1653 op->base.result = device_seek_file(op->base.child, op->requested_file);
1654 op->actual_file = op->base.child->file;
1658 rait_device_seek_file (Device * dself, guint file) {
1663 RaitDevice * self = RAIT_DEVICE(dself);
1664 guint actual_file = 0;
1665 gboolean in_file = FALSE;
1667 if (rait_device_in_error(self)) return NULL;
1669 dself->is_eof = FALSE;
1671 g_mutex_lock(dself->device_mutex);
1672 dself->in_file = FALSE;
1673 dself->bytes_read = 0;
1674 g_mutex_unlock(dself->device_mutex);
1676 ops = g_ptr_array_sized_new(self->private->children->len);
1677 for (i = 0; i < self->private->children->len; i ++) {
1679 if ((int)i == self->private->failed)
1680 continue; /* This device is broken. */
1681 op = g_new(SeekFileOp, 1);
1682 op->base.child = g_ptr_array_index(self->private->children, i);
1683 op->base.child_index = i;
1684 op->requested_file = file;
1685 g_ptr_array_add(ops, op);
1688 do_rait_child_ops(self, seek_file_do_op, ops);
1690 /* This checks for NULL values, but we still have to check for
1691 consistant headers. */
1692 success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1693 ops, extract_boolean_pointer_op);
1696 for (i = 0; i < ops->len; i ++) {
1697 SeekFileOp * this_op;
1698 dumpfile_t * this_result;
1699 guint this_actual_file;
1700 gboolean this_in_file;
1702 this_op = (SeekFileOp*)g_ptr_array_index(ops, i);
1704 if ((signed)this_op->base.child_index == self->private->failed)
1707 this_result = this_op->base.result;
1708 this_actual_file = this_op->actual_file;
1709 this_in_file = this_op->base.child->in_file;
1713 actual_file = this_actual_file;
1714 in_file = this_in_file;
1716 if (headers_are_equal(rval, this_result) &&
1717 actual_file == this_actual_file &&
1718 in_file == this_in_file) {
1727 g_ptr_array_free_full(ops);
1731 /* TODO: be more specific here */
1732 device_set_error(dself,
1733 g_strdup("One or more devices failed to seek_file"),
1734 DEVICE_STATUS_DEVICE_ERROR);
1738 /* update our state */
1739 g_mutex_lock(dself->device_mutex);
1740 dself->in_file = in_file;
1741 g_mutex_unlock(dself->device_mutex);
1742 dself->file = actual_file;
1749 guint64 block; /* IN */
1753 static void seek_block_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1754 SeekBlockOp * op = data;
1756 GINT_TO_POINTER(device_seek_block(op->base.child, op->block));
1760 rait_device_seek_block (Device * dself, guint64 block) {
1765 RaitDevice * self = RAIT_DEVICE(dself);
1767 if (rait_device_in_error(self)) return FALSE;
1769 ops = g_ptr_array_sized_new(self->private->children->len);
1770 for (i = 0; i < self->private->children->len; i ++) {
1772 if ((int)i == self->private->failed)
1773 continue; /* This device is broken. */
1774 op = g_new(SeekBlockOp, 1);
1775 op->base.child = g_ptr_array_index(self->private->children, i);
1776 op->base.child_index = i;
1778 g_ptr_array_add(ops, op);
1781 do_rait_child_ops(self, seek_block_do_op, ops);
1783 success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1784 ops, extract_boolean_generic_op);
1786 g_ptr_array_free_full(ops);
1789 /* TODO: be more specific here */
1790 device_set_error(dself,
1791 stralloc("One or more devices failed to seek_block"),
1792 DEVICE_STATUS_DEVICE_ERROR);
1796 dself->block = block;
1802 gpointer buffer; /* IN */
1803 int read_size; /* IN/OUT -- note not a pointer */
1804 int desired_read_size; /* bookkeeping */
1808 static void read_block_do_op(gpointer data,
1809 gpointer user_data G_GNUC_UNUSED) {
1810 ReadBlockOp * op = data;
1812 GINT_TO_POINTER(device_read_block(op->base.child, op->buffer,
1814 if (op->read_size > op->desired_read_size) {
1815 g_warning("child device %s tried to return an oversized block, which the RAIT device does not support",
1816 op->base.child->device_name);
1820 /* A BooleanExtractor. This one checks for a successful read. */
1821 static gboolean extract_boolean_read_block_op_data(gpointer data) {
1822 ReadBlockOp * op = data;
1823 return GPOINTER_TO_INT(op->base.result) == op->desired_read_size;
1826 /* A BooleanExtractor. This one checks for EOF. */
1827 static gboolean extract_boolean_read_block_op_eof(gpointer data) {
1828 ReadBlockOp * op = data;
1829 return op->base.child->is_eof;
1832 /* Counts the number of elements in an array matching a given proposition. */
1833 static int g_ptr_array_count(GPtrArray * array, BooleanExtractor filter) {
1837 for (i = 0; i < array->len ; i++) {
1838 if (filter(g_ptr_array_index(array, i)))
1844 static gboolean raid_block_reconstruction(RaitDevice * self, GPtrArray * ops,
1845 gpointer buf, size_t bufsize) {
1846 guint num_children, data_children;
1848 gsize child_blocksize;
1851 gpointer parity_block = NULL;
1856 blocksize = DEVICE(self)->block_size;
1857 find_simple_params(self, &num_children, &data_children);
1859 if (num_children > 1)
1860 parity_child = num_children - 1;
1864 child_blocksize = blocksize / data_children;
1866 for (i = 0; i < ops->len; i ++) {
1867 ReadBlockOp * op = g_ptr_array_index(ops, i);
1868 if (!extract_boolean_read_block_op_data(op))
1870 if ((int)(op->base.child_index) == parity_child) {
1871 parity_block = op->buffer;
1873 g_assert(child_blocksize * (op->base.child_index+1) <= bufsize);
1874 memcpy((char *)buf + child_blocksize * op->base.child_index, op->buffer,
1878 if (self->private->status == RAIT_STATUS_COMPLETE) {
1879 g_assert(parity_block != NULL); /* should have found parity_child */
1881 if (num_children >= 2) {
1882 /* Verify the parity block. This code is inefficient but
1883 does the job for the 2-device case, too. */
1884 gpointer constructed_parity;
1885 GPtrArray * data_extents;
1887 constructed_parity = g_malloc(child_blocksize);
1888 data_extents = g_ptr_array_sized_new(data_children);
1889 for (i = 0; i < data_children; i ++) {
1890 ReadBlockOp * op = g_ptr_array_index(ops, i);
1891 g_assert(extract_boolean_read_block_op_data(op));
1892 if ((int)op->base.child_index == parity_child)
1894 g_ptr_array_add(data_extents, op->buffer);
1896 make_parity_block_extents(data_extents, constructed_parity,
1899 if (0 != memcmp(parity_block, constructed_parity,
1901 device_set_error(DEVICE(self),
1902 stralloc(_("RAIT is inconsistent: Parity block did not match data blocks.")),
1903 DEVICE_STATUS_DEVICE_ERROR);
1904 /* TODO: can't we just isolate the device in this case? */
1907 g_ptr_array_free(data_extents, TRUE);
1908 amfree(constructed_parity);
1909 } else { /* do nothing. */ }
1910 } else if (self->private->status == RAIT_STATUS_DEGRADED) {
1911 g_assert(self->private->failed >= 0 && self->private->failed < (int)num_children);
1912 /* We are in degraded mode. What's missing? */
1913 if (self->private->failed == parity_child) {
1915 } else if (num_children >= 2) {
1916 /* Reconstruct failed block from parity block. */
1917 GPtrArray * data_extents = g_ptr_array_new();
1919 for (i = 0; i < data_children; i ++) {
1920 ReadBlockOp * op = g_ptr_array_index(ops, i);
1921 if (!extract_boolean_read_block_op_data(op))
1923 g_ptr_array_add(data_extents, op->buffer);
1926 /* Conveniently, the reconstruction is the same procedure
1927 as the parity generation. This even works if there is
1928 only one remaining device! */
1929 make_parity_block_extents(data_extents,
1930 (char *)buf + (child_blocksize *
1931 self->private->failed),
1934 /* The array members belong to our ops argument. */
1935 g_ptr_array_free(data_extents, TRUE);
1937 g_assert_not_reached();
1940 /* device is already in FAILED state -- we shouldn't even be here */
1947 rait_device_read_block (Device * dself, gpointer buf, int * size) {
1951 guint num_children, data_children;
1952 gsize blocksize = dself->block_size;
1953 gsize child_blocksize;
1955 RaitDevice * self = RAIT_DEVICE(dself);
1957 if (rait_device_in_error(self)) return -1;
1959 find_simple_params(self, &num_children, &data_children);
1961 /* tell caller they haven't given us a big enough buffer */
1962 if (blocksize > (gsize)*size) {
1963 g_assert(blocksize < INT_MAX);
1964 *size = (int)blocksize;
1968 g_assert(blocksize % data_children == 0); /* see find_block_size */
1969 child_blocksize = blocksize / data_children;
1971 ops = g_ptr_array_sized_new(num_children);
1972 for (i = 0; i < num_children; i ++) {
1974 if ((int)i == self->private->failed)
1975 continue; /* This device is broken. */
1976 op = g_new(ReadBlockOp, 1);
1977 op->base.child = g_ptr_array_index(self->private->children, i);
1978 op->base.child_index = i;
1979 op->buffer = g_malloc(child_blocksize);
1980 op->desired_read_size = op->read_size = child_blocksize;
1981 g_ptr_array_add(ops, op);
1984 do_rait_child_ops(self, read_block_do_op, ops);
1986 if (g_ptr_array_count(ops, extract_boolean_read_block_op_data)) {
1987 if (!g_ptr_array_union_robust(RAIT_DEVICE(self),
1989 extract_boolean_read_block_op_data)) {
1990 /* TODO: be more specific */
1991 device_set_error(dself,
1992 stralloc(_("Error occurred combining blocks from child devices")),
1993 DEVICE_STATUS_DEVICE_ERROR);
1996 /* raid_block_reconstruction sets the error status if necessary */
1997 success = raid_block_reconstruction(RAIT_DEVICE(self),
1998 ops, buf, (size_t)*size);
2002 if (g_ptr_array_union_robust(RAIT_DEVICE(self),
2004 extract_boolean_read_block_op_eof)) {
2005 device_set_error(dself,
2007 DEVICE_STATUS_SUCCESS);
2008 dself->is_eof = TRUE;
2009 g_mutex_lock(dself->device_mutex);
2010 dself->in_file = FALSE;
2011 g_mutex_unlock(dself->device_mutex);
2013 device_set_error(dself,
2014 stralloc(_("All child devices failed to read, but not all are at eof")),
2015 DEVICE_STATUS_DEVICE_ERROR);
2019 for (i = 0; i < ops->len; i ++) {
2020 ReadBlockOp * op = g_ptr_array_index(ops, i);
2023 g_ptr_array_free_full(ops);
2028 g_mutex_lock(dself->device_mutex);
2029 dself->bytes_read += blocksize;
2030 g_mutex_unlock(dself->device_mutex);
2037 /* property utility functions */
2041 DevicePropertyId id; /* IN */
2042 GValue value; /* IN/OUT */
2043 PropertySurety surety; /* IN (for set) */
2044 PropertySource source; /* IN (for set) */
2047 /* Creates a GPtrArray of PropertyOf for a get or set operation. */
2048 static GPtrArray * make_property_op_array(RaitDevice * self,
2049 DevicePropertyId id,
2051 PropertySurety surety,
2052 PropertySource source) {
2055 ops = g_ptr_array_sized_new(self->private->children->len);
2056 for (i = 0; i < self->private->children->len; i ++) {
2059 if ((signed)i == self->private->failed) {
2063 op = g_new(PropertyOp, 1);
2064 op->base.child = g_ptr_array_index(self->private->children, i);
2066 bzero(&(op->value), sizeof(op->value));
2067 if (value != NULL) {
2068 g_value_unset_copy(value, &(op->value));
2070 op->surety = surety;
2071 op->source = source;
2072 g_ptr_array_add(ops, op);
2079 static void property_get_do_op(gpointer data,
2080 gpointer user_data G_GNUC_UNUSED) {
2081 PropertyOp * op = data;
2083 bzero(&(op->value), sizeof(op->value));
2085 GINT_TO_POINTER(device_property_get(op->base.child, op->id,
2090 static void property_set_do_op(gpointer data,
2091 gpointer user_data G_GNUC_UNUSED) {
2092 PropertyOp * op = data;
2095 GINT_TO_POINTER(device_property_set_ex(op->base.child, op->id,
2096 &(op->value), op->surety,
2098 g_value_unset(&(op->value));
2101 /* PropertyGetFns and PropertySetFns */
2104 property_get_block_size_fn(Device *dself,
2105 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2106 PropertySurety *surety, PropertySource *source)
2108 RaitDevice *self = RAIT_DEVICE(dself);
2109 gsize my_block_size;
2111 if (dself->block_size_source != PROPERTY_SOURCE_DEFAULT) {
2112 my_block_size = dself->block_size;
2115 *surety = dself->block_size_surety;
2117 gsize child_block_size;
2118 child_block_size = calculate_block_size_from_children(self,
2120 if (child_block_size == 0)
2124 *surety = PROPERTY_SURETY_BAD; /* may still change */
2128 g_value_unset_init(val, G_TYPE_INT);
2129 g_assert(my_block_size < G_MAXINT); /* gsize -> gint */
2130 g_value_set_int(val, (gint)my_block_size);
2134 *source = dself->block_size_source;
2140 property_set_block_size_fn(Device *dself,
2141 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2142 PropertySurety surety, PropertySource source)
2144 RaitDevice *self = RAIT_DEVICE(dself);
2145 gint my_block_size = g_value_get_int(val);
2146 guint data_children;
2148 find_simple_params(self, NULL, &data_children);
2149 if ((my_block_size % data_children) != 0) {
2150 device_set_error(dself,
2151 vstrallocf(_("Block size must be a multiple of %d"), data_children),
2152 DEVICE_STATUS_DEVICE_ERROR);
2156 dself->block_size = my_block_size;
2157 dself->block_size_source = source;
2158 dself->block_size_surety = surety;
2160 if (!fix_block_size(self))
2167 property_get_canonical_name_fn(Device *dself,
2168 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2169 PropertySurety *surety, PropertySource *source)
2171 RaitDevice *self = RAIT_DEVICE(dself);
2172 char *canonical = child_device_names_to_rait_name(self);
2175 g_value_unset_init(val, G_TYPE_STRING);
2176 g_value_set_string(val, canonical);
2181 *surety = PROPERTY_SURETY_GOOD;
2184 *source = PROPERTY_SOURCE_DETECTED;
2190 property_get_concurrency_fn(Device *dself,
2191 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2192 PropertySurety *surety, PropertySource *source)
2194 RaitDevice *self = RAIT_DEVICE(dself);
2195 ConcurrencyParadigm result;
2200 ops = make_property_op_array(self, PROPERTY_CONCURRENCY, NULL, 0, 0);
2201 do_rait_child_ops(self, property_get_do_op, ops);
2203 /* find the most restrictive paradigm acceptable to all
2205 result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
2207 for (i = 0; i < ops->len; i ++) {
2208 ConcurrencyParadigm cur;
2209 PropertyOp * op = g_ptr_array_index(ops, i);
2211 if (!op->base.result
2212 || G_VALUE_TYPE(&(op->value)) != CONCURRENCY_PARADIGM_TYPE) {
2217 cur = g_value_get_enum(&(op->value));
2218 if (result == CONCURRENCY_PARADIGM_EXCLUSIVE ||
2219 cur == CONCURRENCY_PARADIGM_EXCLUSIVE) {
2220 result = CONCURRENCY_PARADIGM_EXCLUSIVE;
2221 } else if (result == CONCURRENCY_PARADIGM_SHARED_READ ||
2222 cur == CONCURRENCY_PARADIGM_SHARED_READ) {
2223 result = CONCURRENCY_PARADIGM_SHARED_READ;
2224 } else if (result == CONCURRENCY_PARADIGM_RANDOM_ACCESS &&
2225 cur == CONCURRENCY_PARADIGM_RANDOM_ACCESS) {
2226 result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
2233 g_ptr_array_free_full(ops);
2237 g_value_unset_init(val, CONCURRENCY_PARADIGM_TYPE);
2238 g_value_set_enum(val, result);
2242 *surety = PROPERTY_SURETY_GOOD;
2245 *source = PROPERTY_SOURCE_DETECTED;
2252 property_get_streaming_fn(Device *dself,
2253 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2254 PropertySurety *surety, PropertySource *source)
2256 RaitDevice *self = RAIT_DEVICE(dself);
2257 StreamingRequirement result;
2262 ops = make_property_op_array(self, PROPERTY_STREAMING, NULL, 0, 0);
2263 do_rait_child_ops(self, property_get_do_op, ops);
2265 /* combine the child streaming requirements, selecting the strongest
2266 * requirement of the bunch. */
2267 result = STREAMING_REQUIREMENT_NONE;
2269 for (i = 0; i < ops->len; i ++) {
2270 StreamingRequirement cur;
2271 PropertyOp * op = g_ptr_array_index(ops, i);
2273 if (!op->base.result
2274 || G_VALUE_TYPE(&(op->value)) != STREAMING_REQUIREMENT_TYPE) {
2279 cur = g_value_get_enum(&(op->value));
2280 if (result == STREAMING_REQUIREMENT_REQUIRED ||
2281 cur == STREAMING_REQUIREMENT_REQUIRED) {
2282 result = STREAMING_REQUIREMENT_REQUIRED;
2283 } else if (result == STREAMING_REQUIREMENT_DESIRED ||
2284 cur == STREAMING_REQUIREMENT_DESIRED) {
2285 result = STREAMING_REQUIREMENT_DESIRED;
2286 } else if (result == STREAMING_REQUIREMENT_NONE &&
2287 cur == STREAMING_REQUIREMENT_NONE) {
2288 result = STREAMING_REQUIREMENT_NONE;
2295 g_ptr_array_free_full(ops);
2299 g_value_unset_init(val, STREAMING_REQUIREMENT_TYPE);
2300 g_value_set_enum(val, result);
2304 *surety = PROPERTY_SURETY_GOOD;
2307 *source = PROPERTY_SOURCE_DETECTED;
2314 property_get_boolean_and_fn(Device *dself,
2315 DevicePropertyBase *base, GValue *val,
2316 PropertySurety *surety, PropertySource *source)
2318 RaitDevice *self = RAIT_DEVICE(dself);
2324 ops = make_property_op_array(self, base->ID, NULL, 0, 0);
2325 do_rait_child_ops(self, property_get_do_op, ops);
2327 /* combine the child values, applying a simple AND */
2330 for (i = 0; i < ops->len; i ++) {
2331 PropertyOp * op = g_ptr_array_index(ops, i);
2333 if (!op->base.result || !G_VALUE_HOLDS_BOOLEAN(&(op->value))) {
2338 if (!g_value_get_boolean(&(op->value))) {
2344 g_ptr_array_free_full(ops);
2348 g_value_unset_init(val, G_TYPE_BOOLEAN);
2349 g_value_set_boolean(val, result);
2353 *surety = PROPERTY_SURETY_GOOD;
2356 *source = PROPERTY_SOURCE_DETECTED;
2363 property_get_medium_access_type_fn(Device *dself,
2364 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2365 PropertySurety *surety, PropertySource *source)
2367 RaitDevice *self = RAIT_DEVICE(dself);
2368 MediaAccessMode result;
2373 ops = make_property_op_array(self, PROPERTY_MEDIUM_ACCESS_TYPE, NULL, 0, 0);
2374 do_rait_child_ops(self, property_get_do_op, ops);
2376 /* combine the modes as best we can */
2379 for (i = 0; i < ops->len; i ++) {
2380 MediaAccessMode cur;
2381 PropertyOp * op = g_ptr_array_index(ops, i);
2383 if (!op->base.result || G_VALUE_TYPE(&(op->value)) != MEDIA_ACCESS_MODE_TYPE) {
2388 cur = g_value_get_enum(&(op->value));
2392 } else if ((result == MEDIA_ACCESS_MODE_READ_ONLY &&
2393 cur == MEDIA_ACCESS_MODE_WRITE_ONLY) ||
2394 (result == MEDIA_ACCESS_MODE_WRITE_ONLY &&
2395 cur == MEDIA_ACCESS_MODE_READ_ONLY)) {
2396 /* Invalid combination; one device can only read, other
2400 } else if (result == MEDIA_ACCESS_MODE_READ_ONLY ||
2401 cur == MEDIA_ACCESS_MODE_READ_ONLY) {
2402 result = MEDIA_ACCESS_MODE_READ_ONLY;
2403 } else if (result == MEDIA_ACCESS_MODE_WRITE_ONLY ||
2404 cur == MEDIA_ACCESS_MODE_WRITE_ONLY) {
2405 result = MEDIA_ACCESS_MODE_WRITE_ONLY;
2406 } else if (result == MEDIA_ACCESS_MODE_WORM ||
2407 cur == MEDIA_ACCESS_MODE_WORM) {
2408 result = MEDIA_ACCESS_MODE_WORM;
2409 } else if (result == MEDIA_ACCESS_MODE_READ_WRITE &&
2410 cur == MEDIA_ACCESS_MODE_READ_WRITE) {
2411 result = MEDIA_ACCESS_MODE_READ_WRITE;
2418 g_ptr_array_free_full(ops);
2422 g_value_unset_init(val, MEDIA_ACCESS_MODE_TYPE);
2423 g_value_set_enum(val, result);
2427 *surety = PROPERTY_SURETY_GOOD;
2430 *source = PROPERTY_SOURCE_DETECTED;
2437 property_get_max_volume_usage_fn(Device *dself,
2438 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2439 PropertySurety *surety, PropertySource *source)
2441 RaitDevice *self = RAIT_DEVICE(dself);
2445 guint data_children;
2447 ops = make_property_op_array(self, PROPERTY_MAX_VOLUME_USAGE, NULL, 0, 0);
2448 do_rait_child_ops(self, property_get_do_op, ops);
2450 /* look for the smallest value that is set */
2452 for (i = 0; i < ops->len; i ++) {
2454 PropertyOp * op = g_ptr_array_index(ops, i);
2456 if (!op->base.result || !G_VALUE_HOLDS_UINT64(&(op->value))) {
2457 continue; /* ignore children without this property */
2460 cur = g_value_get_uint64(&(op->value));
2462 if (!result || (cur && cur < result)) {
2467 g_ptr_array_free_full(ops);
2470 /* result contains the minimum usage on any child. We can use that space
2471 * on each of our data children, so the total is larger */
2472 find_simple_params(self, NULL, &data_children);
2473 result *= data_children;
2476 g_value_unset_init(val, G_TYPE_UINT64);
2477 g_value_set_uint64(val, result);
2481 *surety = PROPERTY_SURETY_GOOD;
2484 *source = PROPERTY_SOURCE_DETECTED;
2488 /* no result from any children, so we effectively don't have this property */
2494 property_set_max_volume_usage_fn(Device *dself,
2495 DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2496 PropertySurety surety, PropertySource source)
2498 RaitDevice *self = RAIT_DEVICE(dself);
2499 guint64 parent_usage;
2500 guint64 child_usage;
2505 guint data_children;
2507 parent_usage = g_value_get_uint64(val);
2508 find_simple_params(self, NULL, &data_children);
2510 child_usage = parent_usage / data_children;
2512 bzero(&child_val, sizeof(child_val));
2513 g_value_init(&child_val, G_TYPE_UINT64);
2514 g_value_set_uint64(&child_val, child_usage);
2516 ops = make_property_op_array(self, PROPERTY_MAX_VOLUME_USAGE,
2517 &child_val, surety, source);
2518 do_rait_child_ops(self, property_set_do_op, ops);
2520 /* if any of the kids succeeded, then we did too */
2522 for (i = 0; i < ops->len; i ++) {
2523 PropertyOp * op = g_ptr_array_index(ops, i);
2525 if (op->base.result) {
2531 g_ptr_array_free_full(ops);
2542 static void recycle_file_do_op(gpointer data,
2543 gpointer user_data G_GNUC_UNUSED) {
2544 RecycleFileOp * op = data;
2546 GINT_TO_POINTER(device_recycle_file(op->base.child, op->filenum));
2550 rait_device_recycle_file (Device * dself, guint filenum) {
2555 RaitDevice * self = RAIT_DEVICE(dself);
2557 if (rait_device_in_error(self)) return FALSE;
2559 ops = g_ptr_array_sized_new(self->private->children->len);
2560 for (i = 0; i < self->private->children->len; i ++) {
2562 op = g_new(RecycleFileOp, 1);
2563 op->base.child = g_ptr_array_index(self->private->children, i);
2564 op->filenum = filenum;
2565 g_ptr_array_add(ops, op);
2568 do_rait_child_ops(self, recycle_file_do_op, ops);
2570 success = g_ptr_array_and(ops, extract_boolean_generic_op);
2572 g_ptr_array_free_full(ops);
2575 /* TODO: be more specific here */
2576 device_set_error(dself,
2577 stralloc(_("One or more devices failed to recycle_file")),
2578 DEVICE_STATUS_DEVICE_ERROR);
2585 static void finish_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
2586 GenericOp * op = data;
2587 op->result = GINT_TO_POINTER(device_finish(op->child));
2591 rait_device_finish (Device * self) {
2594 gboolean rval = TRUE;
2596 rval = !rait_device_in_error(self);
2598 ops = make_generic_boolean_op_array(RAIT_DEVICE(self));
2600 do_rait_child_ops(RAIT_DEVICE(self), finish_do_op, ops);
2602 success = g_ptr_array_and(ops, extract_boolean_generic_op);
2606 g_ptr_array_free_full(ops);
2608 self->access_mode = ACCESS_NULL;
2614 rait_device_factory (char * device_name, char * device_type, char * device_node) {
2616 g_assert(0 == strcmp(device_type, "rait"));
2617 rval = DEVICE(g_object_new(TYPE_RAIT_DEVICE, NULL));
2618 device_open_device(rval, device_name, device_type, device_node);
2623 rait_device_register (void) {
2624 static const char * device_prefix_list[] = {"rait", NULL};
2625 register_device(rait_device_factory, device_prefix_list);