1 /*
2  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
3  *
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.
8  *
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
12  * for more details.
13  *
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
17  *
18  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20  */
21 
22 /* The RAIT device encapsulates some number of other devices into a single
23  * redundant device. */
24 
25 #include "amanda.h"
26 #include "property.h"
27 #include "util.h"
28 #include <glib.h>
29 #include "glib-util.h"
30 #include "device.h"
31 #include "fileheader.h"
32 #include "amsemaphore.h"
33 
34 /* Just a note about the failure mode of different operations:
35    - Recovers from a failure (enters degraded mode)
36      open_device()
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.
40 
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.
44      property functions
45      finish()
46 
47    - Dies in degraded mode (even if remaining devices are OK)
48      start_file()
49      write_block()
50      finish_file()
51      recycle_file()
52 */
53 
54 /*
55  * Type checking and casting macros
56  */
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 ())
62 
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);
65 
66 /*
67  * Main object structure
68  */
69 typedef struct RaitDevice_s {
70     Device __parent__;
71 
72     struct RaitDevicePrivate_s * private;
73 } RaitDevice;
74 
75 /*
76  * Class definition
77  */
78 typedef struct _RaitDeviceClass RaitDeviceClass;
79 struct _RaitDeviceClass {
80     DeviceClass __parent__;
81 };
82 
83 typedef enum {
84     RAIT_STATUS_COMPLETE, /* All subdevices OK. */
85     RAIT_STATUS_DEGRADED, /* One subdevice failed. */
86     RAIT_STATUS_FAILED    /* Two or more subdevices failed. */
87 } RaitStatus;
88 
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.
91  *
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.
97  */
98 #if !(GLIB_CHECK_VERSION(2,10,0))
99 #define USE_INTERNAL_THREADPOOL
100 #endif
101 
102 typedef struct RaitDevicePrivate_s {
103     GPtrArray * children;
104     /* These flags are only relevant for reading. */
105     RaitStatus status;
106     /* If status == RAIT_STATUS_DEGRADED, this holds the index of the
107        failed node. It holds a negative number otherwise. */
108     int failed;
109 
110     /* the child block size */
111     gsize child_block_size;
112 
113 #ifdef USE_INTERNAL_THREADPOOL
114     /* array of ThreadInfo for performing parallel operations */
115     GArray *threads;
116 
117     /* value of this semaphore is the number of threaded operations
118      * in progress */
119     amsemaphore_t *threads_sem;
120 #endif
121 } RaitDevicePrivate;
122 
123 #ifdef USE_INTERNAL_THREADPOOL
124 typedef struct ThreadInfo {
125     GThread *thread;
126 
127     /* struct fields below are protected by this mutex and condition variable */
128     GMutex *mutex;
129     GCond *cond;
130 
131     gboolean die;
132     GFunc func;
133     gpointer data;
134 
135     /* give threads access to active_threads and its mutex/cond */
136     struct RaitDevicePrivate_s *private;
137 } ThreadInfo;
138 #endif
139 
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"
144 
145 #define PRIVATE(o) (o->private)
146 
147 #define rait_device_in_error(dev) \
148     (device_in_error((dev)) || PRIVATE(RAIT_DEVICE((dev)))->status == RAIT_STATUS_FAILED)
149 
150 void rait_device_register (void);
151 
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,
166                                         int * size);
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);
172 
173 /* property handlers */
174 
175 static gboolean property_get_block_size_fn(Device *self,
176     DevicePropertyBase *base, GValue *val,
177     PropertySurety *surety, PropertySource *source);
178 
179 static gboolean property_set_block_size_fn(Device *self,
180     DevicePropertyBase *base, GValue *val,
181     PropertySurety surety, PropertySource source);
182 
183 static gboolean property_get_canonical_name_fn(Device *self,
184     DevicePropertyBase *base, GValue *val,
185     PropertySurety *surety, PropertySource *source);
186 
187 static gboolean property_get_concurrency_fn(Device *self,
188     DevicePropertyBase *base, GValue *val,
189     PropertySurety *surety, PropertySource *source);
190 
191 static gboolean property_get_streaming_fn(Device *self,
192     DevicePropertyBase *base, GValue *val,
193     PropertySurety *surety, PropertySource *source);
194 
195 static gboolean property_get_boolean_and_fn(Device *self,
196     DevicePropertyBase *base, GValue *val,
197     PropertySurety *surety, PropertySource *source);
198 
199 static gboolean property_get_medium_access_type_fn(Device *self,
200     DevicePropertyBase *base, GValue *val,
201     PropertySurety *surety, PropertySource *source);
202 
203 static gboolean property_get_max_volume_usage_fn(Device *self,
204     DevicePropertyBase *base, GValue *val,
205     PropertySurety *surety, PropertySource *source);
206 
207 static gboolean property_set_max_volume_usage_fn(Device *self,
208     DevicePropertyBase *base, GValue *val,
209     PropertySurety surety, PropertySource source);
210 
211 
212 /* pointer to the class of our parent */
213 static DeviceClass *parent_class = NULL;
214 
215 static GType
rait_device_get_type(void)216 rait_device_get_type (void)
217 {
218     static GType type = 0;
219 
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 */,
228             sizeof (RaitDevice),
229             0 /* n_preallocs */,
230             (GInstanceInitFunc) rait_device_init,
231             NULL
232         };
233 
234         type = g_type_register_static (TYPE_DEVICE, "RaitDevice", &info,
235                                        (GTypeFlags)0);
236 	}
237 
238     return type;
239 }
240 
g_object_unref_foreach(gpointer data,gpointer user_data G_GNUC_UNUSED)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);
245     }
246 }
247 
248 static void
rait_device_finalize(GObject * obj_self)249 rait_device_finalize(GObject *obj_self)
250 {
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;
259     }
260 #ifdef USE_INTERNAL_THREADPOOL
261     g_assert(PRIVATE(self)->threads_sem == NULL || PRIVATE(self)->threads_sem->value == 0);
262 
263     if (PRIVATE(self)->threads) {
264 	guint i;
265 
266 	for (i = 0; i < PRIVATE(self)->threads->len; i++) {
267 	    ThreadInfo *inf = &g_array_index(PRIVATE(self)->threads, ThreadInfo, i);
268 	    if (inf->thread) {
269 		/* NOTE: the thread is waiting on this condition right now, not
270 		 * executing an operation. */
271 
272 		/* ask the thread to die */
273 		g_mutex_lock(inf->mutex);
274 		inf->die = TRUE;
275 		g_cond_signal(inf->cond);
276 		g_mutex_unlock(inf->mutex);
277 
278 		/* and wait for it to die, which should happen soon */
279 		g_thread_join(inf->thread);
280 	    }
281 
282 	    if (inf->mutex)
283 		g_mutex_free(inf->mutex);
284 	    if (inf->cond)
285 		g_cond_free(inf->cond);
286 	}
287     }
288 
289     if (PRIVATE(self)->threads_sem)
290 	amsemaphore_free(PRIVATE(self)->threads_sem);
291 #endif
292     amfree(self->private);
293 }
294 
295 static void
rait_device_init(RaitDevice * o G_GNUC_UNUSED)296 rait_device_init (RaitDevice * o G_GNUC_UNUSED)
297 {
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;
305 #endif
306 }
307 
308 static void
rait_device_class_init(RaitDeviceClass * c)309 rait_device_class_init (RaitDeviceClass * c)
310 {
311     GObjectClass *g_object_class = (GObjectClass*) c;
312     DeviceClass *device_class = (DeviceClass *)c;
313 
314     parent_class = g_type_class_ref (TYPE_DEVICE);
315 
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;
328 
329     g_object_class->finalize = rait_device_finalize;
330 
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 */
337     {
338 	GThreadPool *pool = g_thread_pool_new((GFunc)-1, NULL, -1, FALSE, NULL);
339 	g_thread_pool_free(pool, TRUE, FALSE);
340     }
341 #endif
342 
343     g_thread_pool_set_max_unused_threads(-1);
344 #endif
345 }
346 
347 static void
rait_device_base_init(RaitDeviceClass * c)348 rait_device_base_init (RaitDeviceClass * c)
349 {
350     DeviceClass *device_class = (DeviceClass *)c;
351 
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);
358 
359     device_class_register_property(device_class, PROPERTY_CANONICAL_NAME,
360 	    PROPERTY_ACCESS_GET_MASK,
361 	    property_get_canonical_name_fn, NULL);
362 
363     device_class_register_property(device_class, PROPERTY_CONCURRENCY,
364 	    PROPERTY_ACCESS_GET_MASK,
365 	    property_get_concurrency_fn, NULL);
366 
367     device_class_register_property(device_class, PROPERTY_STREAMING,
368 	    PROPERTY_ACCESS_GET_MASK,
369 	    property_get_streaming_fn, NULL);
370 
371     device_class_register_property(device_class, PROPERTY_APPENDABLE,
372 	    PROPERTY_ACCESS_GET_MASK,
373 	    property_get_boolean_and_fn, NULL);
374 
375     device_class_register_property(device_class, PROPERTY_PARTIAL_DELETION,
376 	    PROPERTY_ACCESS_GET_MASK,
377 	    property_get_boolean_and_fn, NULL);
378 
379     device_class_register_property(device_class, PROPERTY_FULL_DELETION,
380 	    PROPERTY_ACCESS_GET_MASK,
381 	    property_get_boolean_and_fn, NULL);
382 
383     device_class_register_property(device_class, PROPERTY_LEOM,
384 	    PROPERTY_ACCESS_GET_MASK,
385 	    property_get_boolean_and_fn, NULL);
386 
387     device_class_register_property(device_class, PROPERTY_MEDIUM_ACCESS_TYPE,
388 	    PROPERTY_ACCESS_GET_MASK,
389 	    property_get_medium_access_type_fn, NULL);
390 
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);
395 }
396 
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.
405  *
406  * When it returns, all the operations have been successfully
407  * executed. If you want results from your operations, do it yourself
408  * through the array.
409  */
410 
411 #ifdef USE_INTERNAL_THREADPOOL
rait_thread_pool_func(gpointer data)412 static gpointer rait_thread_pool_func(gpointer data) {
413     ThreadInfo *inf = data;
414 
415     g_mutex_lock(inf->mutex);
416     while (TRUE) {
417 	while (!inf->die && !inf->func)
418 	    g_cond_wait(inf->cond, inf->mutex);
419 
420 	if (inf->die)
421 	    break;
422 
423 	if (inf->func) {
424 	    /* invoke the function */
425 	    inf->func(inf->data, NULL);
426 	    inf->func = NULL;
427 	    inf->data = NULL;
428 
429             /* indicate that we're finished; will not block */
430 	    amsemaphore_down(inf->private->threads_sem);
431 	}
432     }
433     g_mutex_unlock(inf->mutex);
434     return NULL;
435 }
436 
do_thread_pool_op(RaitDevice * self,GFunc func,GPtrArray * ops)437 static void do_thread_pool_op(RaitDevice *self, GFunc func, GPtrArray * ops) {
438     guint i;
439 
440     if (PRIVATE(self)->threads_sem == NULL)
441 	PRIVATE(self)->threads_sem = amsemaphore_new_with_value(0);
442 
443     if (PRIVATE(self)->threads == NULL)
444 	PRIVATE(self)->threads = g_array_sized_new(FALSE, TRUE,
445 					    sizeof(ThreadInfo), ops->len);
446 
447     g_assert(PRIVATE(self)->threads_sem->value == 0);
448 
449     if (PRIVATE(self)->threads->len < ops->len)
450 	g_array_set_size(PRIVATE(self)->threads, ops->len);
451 
452     /* the semaphore will hit zero when each thread has decremented it */
453     amsemaphore_force_set(PRIVATE(self)->threads_sem, ops->len);
454 
455     for (i = 0; i < ops->len; i++) {
456 	ThreadInfo *inf = &g_array_index(PRIVATE(self)->threads, ThreadInfo, i);
457 	if (!inf->thread) {
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);
462 	}
463 
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);
467 	inf->func = func;
468 	g_cond_signal(inf->cond);
469 	g_mutex_unlock(inf->mutex);
470     }
471 
472     /* wait until semaphore hits zero */
473     amsemaphore_wait_empty(PRIVATE(self)->threads_sem);
474 }
475 
476 #else /* USE_INTERNAL_THREADPOOL */
477 
do_thread_pool_op(RaitDevice * self G_GNUC_UNUSED,GFunc func,GPtrArray * ops)478 static void do_thread_pool_op(RaitDevice *self G_GNUC_UNUSED, GFunc func, GPtrArray * ops) {
479     GThreadPool * pool;
480     guint i;
481 
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);
485     }
486 
487     g_thread_pool_free(pool, FALSE, TRUE);
488 }
489 
490 #endif /* USE_INTERNAL_THREADPOOL */
491 
492 /* This does the above, in a serial fashion (and without using threads) */
do_unthreaded_ops(RaitDevice * self G_GNUC_UNUSED,GFunc func,GPtrArray * ops)493 static void do_unthreaded_ops(RaitDevice *self G_GNUC_UNUSED, GFunc func, GPtrArray * ops) {
494     guint i;
495 
496     for (i = 0; i < ops->len; i ++) {
497         func(g_ptr_array_index(ops, i), NULL);
498     }
499 }
500 
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(). */
do_rait_child_ops(RaitDevice * self,GFunc func,GPtrArray * ops)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);
507     } else {
508         do_unthreaded_ops(self, func, ops);
509     }
510 }
511 
512 static char *
child_device_names_to_rait_name(RaitDevice * self)513 child_device_names_to_rait_name(RaitDevice * self) {
514     GPtrArray *kids;
515     char *braced, *result;
516     guint i;
517 
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;
522         GValue val;
523 	gboolean got_prop = FALSE;
524 
525         bzero(&val, sizeof(val));
526 
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);
530 		got_prop = TRUE;
531 	    }
532 	}
533 
534 	if (!got_prop)
535             child_name = "MISSING";
536 
537 	g_ptr_array_add(kids, g_strdup(child_name));
538 
539 	if (got_prop)
540 	    g_value_unset(&val);
541     }
542 
543     braced = collapse_braced_alternates(kids);
544     result = g_strdup_printf("rait:%s", braced);
545     g_free(braced);
546 
547     return result;
548 }
549 
550 /* Find a workable child block size, based on the block size ranges of our
551  * child devices.
552  *
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
557  * size ranges.
558 
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.
563  */
564 static gsize
calculate_block_size_from_children(RaitDevice * self,gsize * rait_size)565 calculate_block_size_from_children(RaitDevice * self, gsize *rait_size)
566 {
567     gsize min = 0;
568     gsize max = SIZE_MAX;
569     gboolean found_one = FALSE;
570     gsize result;
571     guint i;
572 
573     for (i = 0; i < self->private->children->len; i ++) {
574         gsize child_min = SIZE_MAX, child_max = 0;
575 	Device *child;
576         GValue property_result;
577 	PropertySource source;
578 
579         bzero(&property_result, sizeof(property_result));
580 
581 	if ((signed)i == self->private->failed)
582 	    continue;
583 
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));
589             continue;
590 	}
591 
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);
596 	} else {
597 	    if (!device_property_get(child, PROPERTY_MIN_BLOCK_SIZE,
598 				     &property_result)) {
599 		g_warning("Error getting MIN_BLOCK_SIZE from %s: %s",
600 			child->device_name, device_error_or_status(child));
601 		continue;
602 	    }
603 	    child_min = g_value_get_uint(&property_result);
604 
605 	    if (!device_property_get(child, PROPERTY_MAX_BLOCK_SIZE,
606 				     &property_result)) {
607 		g_warning("Error getting MAX_BLOCK_SIZE from %s: %s",
608 			child->device_name, device_error_or_status(child));
609 		continue;
610 	    }
611 	    child_max = g_value_get_uint(&property_result);
612 
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);
615 		continue;
616 	    }
617 	}
618 
619 	found_one = TRUE;
620 	min = MAX(min, child_min);
621 	max = MIN(max, child_max);
622     }
623 
624     if (!found_one) {
625 	device_set_error((Device*)self,
626 	    stralloc(_("Could not find any child devices' block size ranges")),
627 	    DEVICE_STATUS_DEVICE_ERROR);
628 	return 0;
629     }
630 
631     if (min > max) {
632 	device_set_error((Device*)self,
633 	    stralloc(_("No block size is acceptable to all child devices")),
634 	    DEVICE_STATUS_DEVICE_ERROR);
635 	return 0;
636     }
637 
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);
641 
642     if (rait_size) {
643 	guint data_children;
644 	find_simple_params(self, NULL, &data_children);
645 	*rait_size = result * data_children;
646     }
647 
648     return result;
649 }
650 
651 /* Set BLOCK_SIZE on all children */
652 static gboolean
set_block_size_on_children(RaitDevice * self,gsize child_block_size)653 set_block_size_on_children(RaitDevice *self, gsize child_block_size)
654 {
655     GValue val;
656     guint i;
657     PropertySource source;
658 
659     bzero(&val, sizeof(val));
660 
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);
664 
665     for (i = 0; i < self->private->children->len; i ++) {
666 	Device *child;
667 	GValue property_result;
668 
669 	bzero(&property_result, sizeof(property_result));
670 
671 	if ((signed)i == self->private->failed)
672 	    continue;
673 
674 	child = g_ptr_array_index(self->private->children, i);
675 
676 	/* first, make sure the block size is at its default, or is already
677 	 * correct */
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);
688 		return FALSE;
689 	    }
690 	} else {
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));
694 	}
695 
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);
700 	    return FALSE;
701 	}
702     }
703 
704     return TRUE;
705 }
706 
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. */
710 static gboolean
fix_block_size(RaitDevice * self)711 fix_block_size(RaitDevice *self)
712 {
713     Device *dself = (Device *)self;
714     gsize my_block_size, child_block_size;
715 
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)
719 	    return FALSE;
720 
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;
725     } else {
726 	guint data_children;
727 
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;
731     }
732 
733     /* now tell the children we mean it */
734     if (!set_block_size_on_children(self, child_block_size))
735 	return FALSE;
736 
737     return TRUE;
738 }
739 
740 /* This structure contains common fields for many operations. Not all
741    operations use all fields, however. */
742 typedef struct {
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
746                         operations. */
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
750                           array. */
751 } GenericOp;
752 
753 typedef gboolean (*BooleanExtractor)(gpointer data);
754 
755 /* A BooleanExtractor */
extract_boolean_generic_op(gpointer data)756 static gboolean extract_boolean_generic_op(gpointer data) {
757     GenericOp * op = data;
758     return GPOINTER_TO_INT(op->result);
759 }
760 
761 /* A BooleanExtractor */
extract_boolean_pointer_op(gpointer data)762 static gboolean extract_boolean_pointer_op(gpointer data) {
763     GenericOp * op = data;
764     return op->result != NULL;
765 }
766 
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.
773 */
g_ptr_array_and(GPtrArray * array,BooleanExtractor extractor)774 static gboolean g_ptr_array_and(GPtrArray * array,
775                                 BooleanExtractor extractor) {
776     guint i;
777     if (array == NULL || array->len <= 0)
778         return FALSE;
779 
780     for (i = 0; i < array->len; i ++) {
781         if (!extractor(g_ptr_array_index(array, i)))
782             return FALSE;
783     }
784 
785     return TRUE;
786 }
787 
788 /* Takes a RaitDevice, and makes a GPtrArray of GenericOp. */
make_generic_boolean_op_array(RaitDevice * self)789 static GPtrArray * make_generic_boolean_op_array(RaitDevice* self) {
790     GPtrArray * rval;
791     guint i;
792 
793     rval = g_ptr_array_sized_new(self->private->children->len);
794     for (i = 0; i < self->private->children->len; i ++) {
795         GenericOp * op;
796 
797         if ((signed)i == self->private->failed) {
798             continue;
799         }
800 
801         op = g_new(GenericOp, 1);
802         op->child = g_ptr_array_index(self->private->children, i);
803         op->child_index = i;
804         g_ptr_array_add(rval, op);
805     }
806 
807     return rval;
808 }
809 
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
813    occured. */
g_ptr_array_union_robust(RaitDevice * self,GPtrArray * ops,BooleanExtractor extractor)814 static gboolean g_ptr_array_union_robust(RaitDevice * self, GPtrArray * ops,
815                                          BooleanExtractor extractor) {
816     int nfailed = 0;
817     int lastfailed = 0;
818     guint i;
819 
820     /* We found one or more failed elements.  See which elements failed, and
821      * isolate them*/
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));
830 	    nfailed++;
831             lastfailed = i;
832 	}
833     }
834 
835     /* no failures? great! */
836     if (nfailed == 0)
837 	return TRUE;
838 
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);
844 	return TRUE;
845     } else {
846 	self->private->status = RAIT_STATUS_FAILED;
847 	g_warning("RAIT array %s FAILED", DEVICE(self)->device_name);
848 	return FALSE;
849     }
850 }
851 
852 typedef struct {
853     RaitDevice * self;
854     char *rait_name;
855     char * device_name; /* IN */
856     Device * result;    /* OUT */
857 } OpenDeviceOp;
858 
859 /* A GFunc. */
device_open_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)860 static void device_open_do_op(gpointer data,
861                               gpointer user_data G_GNUC_UNUSED) {
862     OpenDeviceOp * op = data;
863 
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);
869         op->result = NULL;
870     } else {
871         op->result = device_open(op->device_name);
872     }
873 }
874 
875 /* Returns TRUE if and only if the volume label and time are equal. */
compare_volume_results(Device * a,Device * b)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));
879 }
880 
881 /* Stickes new_message at the end of *old_message; frees new_message and
882  * may change *old_message. */
append_message(char ** old_message,char * new_message)883 static void append_message(char ** old_message, char * new_message) {
884     char * rval;
885     if (*old_message == NULL || **old_message == '\0') {
886         rval = new_message;
887     } else {
888         rval = g_strdup_printf("%s; %s", *old_message, new_message);
889         amfree(new_message);
890     }
891     amfree(*old_message);
892     *old_message = rval;
893 }
894 
895 static gboolean
open_child_devices(Device * dself,char * device_name,char * device_node)896 open_child_devices (Device * dself, char * device_name,
897 	    char * device_node) {
898     GPtrArray *device_names;
899     GPtrArray * device_open_ops;
900     guint i;
901     gboolean failure;
902     char *failure_errmsgs;
903     DeviceStatusFlags failure_flags;
904     RaitDevice * self;
905 
906     self = RAIT_DEVICE(dself);
907 
908     device_names = expand_braced_alternates(device_node);
909 
910     if (device_names == NULL) {
911 	device_set_error(dself,
912 	    vstrallocf(_("Invalid RAIT device name '%s'"), device_name),
913 	    DEVICE_STATUS_DEVICE_ERROR);
914         return FALSE;
915     }
916 
917     /* Open devices in a separate thread, in case they have to rewind etc. */
918     device_open_ops = g_ptr_array_new();
919 
920     for (i = 0; i < device_names->len; i++) {
921         OpenDeviceOp *op;
922 	char *name = g_ptr_array_index(device_names, i);
923 
924         op = g_new(OpenDeviceOp, 1);
925         op->device_name = name;
926         op->result = NULL;
927         op->self = self;
928 	op->rait_name = device_name;
929         g_ptr_array_add(device_open_ops, op);
930     }
931 
932     g_ptr_array_free(device_names, TRUE);
933     do_rait_child_ops(self, device_open_do_op, device_open_ops);
934 
935     failure = FALSE;
936     failure_errmsgs = NULL;
937     failure_flags = 0;
938 
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);
942 
943         if (op->result != NULL &&
944             op->result->status == DEVICE_STATUS_SUCCESS) {
945 	    g_ptr_array_add(self->private->children, op->result);
946         } else {
947             char * this_failure_errmsg =
948                 g_strdup_printf("%s: %s", op->device_name,
949                                 device_error_or_status(op->result));
950             DeviceStatusFlags status =
951                 op->result == NULL ?
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. */
958                 g_warning("%s: %s",
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;
965             } else {
966                 /* The second and further failures are fatal. */
967                 failure = TRUE;
968             }
969         }
970         amfree(op->device_name);
971     }
972 
973     g_ptr_array_free_full(device_open_ops);
974 
975     if (failure) {
976         self->private->status = RAIT_STATUS_FAILED;
977 	device_set_error(dself, failure_errmsgs, failure_flags);
978         return FALSE;
979     }
980 
981     return TRUE;
982 }
983 
984 static void
rait_device_open_device(Device * dself,char * device_name,char * device_type G_GNUC_UNUSED,char * device_node)985 rait_device_open_device (Device * dself, char * device_name,
986 	    char * device_type G_GNUC_UNUSED, char * device_node) {
987 
988     if (0 != strcmp(device_node, DEFER_CHILDREN_SENTINEL)) {
989 	if (!open_child_devices(dself, device_name, device_node))
990 	    return;
991 
992 	/* Chain up. */
993 	if (parent_class->open_device) {
994 	    parent_class->open_device(dself, device_name, device_type, device_node);
995 	}
996     }
997 }
998 
999 Device *
rait_device_open_from_children(GSList * child_devices)1000 rait_device_open_from_children (GSList *child_devices) {
1001     Device *dself;
1002     RaitDevice *self;
1003     GSList *iter;
1004     char *device_name;
1005     int nfailures;
1006     int i;
1007 
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)) {
1011 	return dself;
1012     }
1013 
1014     /* set its children */
1015     self = RAIT_DEVICE(dself);
1016     nfailures = 0;
1017     for (i=0, iter = child_devices; iter; i++, iter = iter->next) {
1018 	Device *kid = iter->data;
1019 
1020 	/* a NULL kid is OK -- it opens the device in degraded mode */
1021 	if (!kid) {
1022 	    nfailures++;
1023 	    self->private->failed = i;
1024 	} else {
1025 	    g_assert(IS_DEVICE(kid));
1026 	    g_object_ref((GObject *)kid);
1027 	}
1028 
1029 	g_ptr_array_add(self->private->children, kid);
1030     }
1031 
1032     /* and set the status based on the children */
1033     switch (nfailures) {
1034 	case 0:
1035 	    self->private->status = RAIT_STATUS_COMPLETE;
1036 	    break;
1037 
1038 	case 1:
1039 	    self->private->status = RAIT_STATUS_DEGRADED;
1040 	    break;
1041 
1042 	default:
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);
1047 	    break;
1048     }
1049 
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);
1053 
1054     if (parent_class->open_device) {
1055 	parent_class->open_device(dself,
1056 	    device_name, "rait",
1057 	    device_name+5); /* (+5 skips "rait:") */
1058     }
1059 
1060     return dself;
1061 }
1062 
1063 /* A GFunc. */
read_label_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)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));
1068 }
1069 
rait_device_read_label(Device * dself)1070 static DeviceStatusFlags rait_device_read_label(Device * dself) {
1071     RaitDevice * self;
1072     GPtrArray * ops;
1073     DeviceStatusFlags failed_result = 0;
1074     char *failed_errmsg = NULL;
1075     unsigned int i;
1076     Device * first_success = NULL;
1077 
1078     self = RAIT_DEVICE(dself);
1079 
1080     amfree(dself->volume_time);
1081     amfree(dself->volume_label);
1082     dumpfile_free(dself->volume_header);
1083     dself->volume_header = NULL;
1084 
1085     if (rait_device_in_error(self))
1086         return dself->status | DEVICE_STATUS_DEVICE_ERROR;
1087 
1088     /* nail down our block size, if we haven't already */
1089     if (!fix_block_size(self))
1090 	return FALSE;
1091 
1092     ops = make_generic_boolean_op_array(self);
1093 
1094     do_rait_child_ops(self, read_label_do_op, ops);
1095 
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 (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;
1115             }
1116         } else {
1117             failed_result |= result;
1118         }
1119     }
1120 
1121     if (failed_result != DEVICE_STATUS_SUCCESS) {
1122         /* We had multiple failures or an inconsistency. */
1123 	device_set_error(dself, failed_errmsg, failed_result);
1124     } else {
1125         /* Everything peachy. */
1126 	amfree(failed_errmsg);
1127 
1128         g_assert(first_success != NULL);
1129         if (first_success->volume_label != NULL) {
1130             dself->volume_label = g_strdup(first_success->volume_label);
1131         }
1132         if (first_success->volume_time != NULL) {
1133             dself->volume_time = g_strdup(first_success->volume_time);
1134         }
1135         if (first_success->volume_header != NULL) {
1136             dself->volume_header = dumpfile_copy(first_success->volume_header);
1137         }
1138 	dself->header_block_size = first_success->header_block_size;
1139     }
1140 
1141     g_ptr_array_free_full(ops);
1142 
1143     return dself->status;
1144 }
1145 
1146 typedef struct {
1147     GenericOp base;
1148     DeviceAccessMode mode; /* IN */
1149     char * label;          /* IN */
1150     char * timestamp;      /* IN */
1151 } StartOp;
1152 
1153 /* A GFunc. */
start_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)1154 static void start_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1155     DeviceClass *klass;
1156     StartOp * param = data;
1157 
1158     klass = DEVICE_GET_CLASS(param->base.child);
1159     if (klass->start) {
1160         param->base.result =
1161             GINT_TO_POINTER((klass->start)(param->base.child,
1162                                             param->mode, param->label,
1163                                             param->timestamp));
1164     } else {
1165         param->base.result = FALSE;
1166     }
1167 }
1168 
1169 static gboolean
rait_device_configure(Device * dself,gboolean use_global_config)1170 rait_device_configure(Device * dself, gboolean use_global_config)
1171 {
1172     RaitDevice *self = RAIT_DEVICE(dself);
1173     guint i;
1174 
1175     for (i = 0; i < self->private->children->len; i ++) {
1176 	Device *child;
1177 
1178 	if ((signed)i == self->private->failed)
1179 	    continue;
1180 
1181 	child = g_ptr_array_index(self->private->children, i);
1182 	/* unconditionally configure the child without the global
1183 	 * configuration */
1184 	if (!device_configure(child, FALSE))
1185 	    return FALSE;
1186     }
1187 
1188     if (parent_class->configure) {
1189         return parent_class->configure(dself, use_global_config);
1190     }
1191 
1192     return TRUE;
1193 }
1194 
1195 static gboolean
rait_device_start(Device * dself,DeviceAccessMode mode,char * label,char * timestamp)1196 rait_device_start (Device * dself, DeviceAccessMode mode, char * label,
1197                    char * timestamp) {
1198     GPtrArray * ops;
1199     guint i;
1200     gboolean success;
1201     RaitDevice * self;
1202     DeviceStatusFlags total_status;
1203     char *failure_errmsgs = NULL;
1204     char * label_from_device = NULL;
1205 
1206     self = RAIT_DEVICE(dself);
1207 
1208     if (rait_device_in_error(self)) return FALSE;
1209 
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);
1218         return FALSE;
1219     }
1220 
1221     /* nail down our block size, if we haven't already */
1222     if (!fix_block_size(self))
1223 	return FALSE;
1224 
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;
1233 
1234     ops = g_ptr_array_sized_new(self->private->children->len);
1235     for (i = 0; i < self->private->children->len; i ++) {
1236         StartOp * op;
1237 
1238         if ((signed)i == self->private->failed) {
1239             continue;
1240         }
1241 
1242         op = g_new(StartOp, 1);
1243         op->base.child = g_ptr_array_index(self->private->children, i);
1244         op->mode = mode;
1245         op->label = g_strdup(label);
1246         op->timestamp = g_strdup(timestamp);
1247         g_ptr_array_add(ops, op);
1248     }
1249 
1250     do_rait_child_ops(self, start_do_op, ops);
1251 
1252     success = g_ptr_array_and(ops, extract_boolean_generic_op);
1253 
1254     /* Check results of starting devices; this is mostly about the
1255      * VOLUME_UNLABELED flag. */
1256     total_status = 0;
1257     for (i = 0; i < ops->len; i ++) {
1258         StartOp * op = g_ptr_array_index(ops, i);
1259         Device *child = op->base.child;
1260 
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",
1266                                            child->device_name,
1267                                            device_error_or_status(child)));
1268         } else {
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 "
1277                                             "device %s",
1278                                             child->device_name,
1279                                             child->volume_label,
1280                                             child->volume_time,
1281                                             dself->volume_label,
1282                                             dself->volume_time,
1283                                             label_from_device);
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");
1287                     }
1288                 } else {
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);
1294                 }
1295             } else {
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;
1303             }
1304 	}
1305     }
1306 
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;
1311     }
1312 
1313     amfree(label_from_device);
1314     g_ptr_array_free_full(ops);
1315 
1316     dself->status = total_status;
1317 
1318     if (total_status != DEVICE_STATUS_SUCCESS || !success) {
1319 	device_set_error(dself, failure_errmsgs, total_status);
1320         return FALSE;
1321     }
1322     amfree(failure_errmsgs);
1323     return TRUE;
1324 }
1325 
1326 typedef struct {
1327     GenericOp base;
1328     dumpfile_t * info; /* IN */
1329     int fileno;
1330 } StartFileOp;
1331 
1332 /* a GFunc */
start_file_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)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,
1336                                                         op->info));
1337     op->fileno = op->base.child->file;
1338     if (op->fileno < 1) {
1339         op->base.result = FALSE;
1340     }
1341 }
1342 
1343 static gboolean
rait_device_start_file(Device * dself,dumpfile_t * info)1344 rait_device_start_file (Device * dself, dumpfile_t * info) {
1345     GPtrArray * ops;
1346     guint i;
1347     gboolean success;
1348     RaitDevice * self;
1349     int actual_file = -1;
1350 
1351     self = RAIT_DEVICE(dself);
1352 
1353     if (rait_device_in_error(self)) return FALSE;
1354     if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1355 
1356     ops = g_ptr_array_sized_new(self->private->children->len);
1357     for (i = 0; i < self->private->children->len; i ++) {
1358         StartFileOp * op;
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);
1365     }
1366 
1367     do_rait_child_ops(self, start_file_do_op, ops);
1368 
1369     success = g_ptr_array_and(ops, extract_boolean_generic_op);
1370 
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)
1374             continue;
1375         g_assert(op->fileno >= 1);
1376         if (actual_file < 1) {
1377             actual_file = op->fileno;
1378         }
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 "
1386                                              "file number %d.",
1387                                              op->base.child->device_name,
1388                                              op->fileno, actual_file),
1389                              DEVICE_STATUS_DEVICE_ERROR);
1390             success = FALSE;
1391             op->base.result = FALSE;
1392         }
1393     }
1394 
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);
1398     }
1399     g_ptr_array_free_full(ops);
1400 
1401     if (!success) {
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);
1406         }
1407         return FALSE;
1408     }
1409 
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);
1416 
1417     return TRUE;
1418 }
1419 
find_simple_params(RaitDevice * self,guint * num_children,guint * data_children)1420 static void find_simple_params(RaitDevice * self,
1421                                guint * num_children,
1422                                guint * data_children) {
1423     int num, data;
1424 
1425     num = self->private->children->len;
1426     if (num > 1)
1427         data = num - 1;
1428     else
1429         data = num;
1430     if (num_children != NULL)
1431         *num_children = num;
1432     if (data_children != NULL)
1433         *data_children = data;
1434 }
1435 
1436 typedef struct {
1437     GenericOp base;
1438     guint size;           /* IN */
1439     gpointer data;        /* IN */
1440     gboolean data_needs_free; /* bookkeeping */
1441 } WriteBlockOp;
1442 
1443 /* a GFunc. */
write_block_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)1444 static void write_block_do_op(gpointer data,
1445                               gpointer user_data G_GNUC_UNUSED) {
1446     WriteBlockOp * op = data;
1447 
1448     op->base.result =
1449         GINT_TO_POINTER(device_write_block(op->base.child, op->size, op->data));
1450 }
1451 
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)
1457  */
make_parity_block(char * data,char * parity,guint chunk_size,guint num_chunks)1458 static void make_parity_block(char * data, char * parity,
1459                               guint chunk_size, guint num_chunks) {
1460     guint i;
1461     bzero(parity, chunk_size);
1462     for (i = 0; i < num_chunks - 1; i ++) {
1463         guint j;
1464         for (j = 0; j < chunk_size; j ++) {
1465             parity[j] ^= data[chunk_size*i + j];
1466         }
1467     }
1468 }
1469 
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
1472    chunks. */
make_parity_block_extents(GPtrArray * data,char * parity,guint chunk_size)1473 static void make_parity_block_extents(GPtrArray * data, char * parity,
1474                                       guint chunk_size) {
1475     guint i;
1476     bzero(parity, chunk_size);
1477     for (i = 0; i < data->len; i ++) {
1478         guint j;
1479         char * data_chunk;
1480         data_chunk = g_ptr_array_index(data, i);
1481         for (j = 0; j < chunk_size; j ++) {
1482             parity[j] ^= data_chunk[j];
1483         }
1484     }
1485 }
1486 
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. */
extract_data_block(char * data,guint size,guint chunks,guint chunk)1489 static char * extract_data_block(char * data, guint size,
1490                                  guint chunks, guint chunk) {
1491     char * rval;
1492     guint chunk_size;
1493 
1494     g_assert(chunks > 0 && chunk > 0 && chunk <= chunks);
1495     g_assert(data != NULL);
1496     g_assert(size > 0 && size % (chunks - 1) == 0);
1497 
1498     chunk_size = size / (chunks - 1);
1499     rval = g_malloc(chunk_size);
1500     if (chunks != chunk) {
1501         /* data block. */
1502         memcpy(rval, data + chunk_size * (chunk - 1), chunk_size);
1503     } else {
1504         make_parity_block(data, rval, chunk_size, chunks);
1505     }
1506 
1507     return rval;
1508 }
1509 
1510 static gboolean
rait_device_write_block(Device * dself,guint size,gpointer data)1511 rait_device_write_block (Device * dself, guint size, gpointer data) {
1512     GPtrArray * ops;
1513     guint i;
1514     gboolean success;
1515     guint data_children, num_children;
1516     gsize blocksize = dself->block_size;
1517     RaitDevice * self;
1518     gboolean last_block = (size < blocksize);
1519 
1520     self = RAIT_DEVICE(dself);
1521 
1522     if (rait_device_in_error(self)) return FALSE;
1523     if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1524 
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;
1529     else
1530         data_children = num_children;
1531 
1532     g_assert(size % data_children == 0 || last_block);
1533 
1534     /* zero out to the end of a short block -- tape devices only write
1535      * whole blocks. */
1536     if (last_block) {
1537         char *new_data;
1538 
1539         new_data = g_malloc(blocksize);
1540         memcpy(new_data, data, size);
1541         bzero(new_data + size, blocksize - size);
1542 
1543         data = new_data;
1544         size = blocksize;
1545     }
1546 
1547     ops = g_ptr_array_sized_new(num_children);
1548     for (i = 0; i < self->private->children->len; i ++) {
1549         WriteBlockOp * op;
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) {
1554             op->data = data;
1555             op->data_needs_free = FALSE;
1556         } else {
1557             op->data_needs_free = TRUE;
1558             op->data = extract_data_block(data, size, num_children, i + 1);
1559         }
1560         g_ptr_array_add(ops, op);
1561     }
1562 
1563     do_rait_child_ops(self, write_block_do_op, ops);
1564 
1565     success = g_ptr_array_and(ops, extract_boolean_generic_op);
1566 
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)
1570             free(op->data);
1571     }
1572 
1573     if (last_block) {
1574         amfree(data);
1575     }
1576 
1577     g_ptr_array_free_full(ops);
1578 
1579     if (!success) {
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;
1590         return FALSE;
1591     } else {
1592         dself->block ++;
1593 	g_mutex_lock(dself->device_mutex);
1594 	dself->bytes_written += size;
1595 	g_mutex_unlock(dself->device_mutex);
1596 
1597         return TRUE;
1598     }
1599 }
1600 
1601 /* A GFunc */
finish_file_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)1602 static void finish_file_do_op(gpointer data,
1603                               gpointer user_data G_GNUC_UNUSED) {
1604     GenericOp * op = data;
1605     if (op->child) {
1606         op->result = GINT_TO_POINTER(device_finish_file(op->child));
1607     } else {
1608         op->result = FALSE;
1609     }
1610 }
1611 
1612 static gboolean
rait_device_finish_file(Device * dself)1613 rait_device_finish_file (Device * dself) {
1614     GPtrArray * ops;
1615     gboolean success;
1616     RaitDevice * self = RAIT_DEVICE(dself);
1617 
1618     g_assert(self != NULL);
1619     if (!dself->in_file)
1620 	return TRUE;
1621 
1622     if (rait_device_in_error(dself)) return FALSE;
1623     if (self->private->status != RAIT_STATUS_COMPLETE) return FALSE;
1624 
1625     ops = make_generic_boolean_op_array(self);
1626 
1627     do_rait_child_ops(self, finish_file_do_op, ops);
1628 
1629     success = g_ptr_array_and(ops, extract_boolean_generic_op);
1630 
1631     g_ptr_array_free_full(ops);
1632 
1633     if (!success) {
1634 	/* TODO: be more specific here */
1635 	device_set_error(dself,
1636                          g_strdup("One or more devices failed to finish_file"),
1637 	    DEVICE_STATUS_DEVICE_ERROR);
1638         return FALSE;
1639     }
1640 
1641     g_mutex_lock(dself->device_mutex);
1642     dself->in_file = FALSE;
1643     g_mutex_unlock(dself->device_mutex);
1644     return TRUE;
1645 }
1646 
1647 typedef struct {
1648     GenericOp base;
1649     guint requested_file;                /* IN */
1650     guint actual_file;                   /* OUT */
1651 } SeekFileOp;
1652 
1653 /* a GFunc. */
seek_file_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)1654 static void seek_file_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1655     SeekFileOp * op = data;
1656     op->base.result = device_seek_file(op->base.child, op->requested_file);
1657     op->actual_file = op->base.child->file;
1658 }
1659 
1660 static dumpfile_t *
rait_device_seek_file(Device * dself,guint file)1661 rait_device_seek_file (Device * dself, guint file) {
1662     GPtrArray * ops;
1663     guint i;
1664     gboolean success;
1665     dumpfile_t * rval;
1666     RaitDevice * self = RAIT_DEVICE(dself);
1667     guint actual_file = 0;
1668     gboolean in_file = FALSE;
1669 
1670     if (rait_device_in_error(self)) return NULL;
1671 
1672     dself->is_eof = FALSE;
1673     dself->block = 0;
1674     g_mutex_lock(dself->device_mutex);
1675     dself->in_file = FALSE;
1676     dself->bytes_read = 0;
1677     g_mutex_unlock(dself->device_mutex);
1678 
1679     ops = g_ptr_array_sized_new(self->private->children->len);
1680     for (i = 0; i < self->private->children->len; i ++) {
1681         SeekFileOp * op;
1682         if ((int)i == self->private->failed)
1683             continue; /* This device is broken. */
1684         op = g_new(SeekFileOp, 1);
1685         op->base.child = g_ptr_array_index(self->private->children, i);
1686         op->base.child_index = i;
1687         op->requested_file = file;
1688         g_ptr_array_add(ops, op);
1689     }
1690 
1691     do_rait_child_ops(self, seek_file_do_op, ops);
1692 
1693     /* This checks for NULL values, but we still have to check for
1694        consistant headers. */
1695     success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1696                                        ops, extract_boolean_pointer_op);
1697 
1698     rval = NULL;
1699     for (i = 0; i < ops->len; i ++) {
1700         SeekFileOp * this_op;
1701         dumpfile_t * this_result;
1702         guint this_actual_file;
1703 	gboolean this_in_file;
1704 
1705         this_op = (SeekFileOp*)g_ptr_array_index(ops, i);
1706 
1707         if ((signed)this_op->base.child_index == self->private->failed)
1708             continue;
1709 
1710         this_result = this_op->base.result;
1711         this_actual_file = this_op->actual_file;
1712 	this_in_file = this_op->base.child->in_file;
1713 
1714         if (rval == NULL) {
1715             rval = this_result;
1716             actual_file = this_actual_file;
1717 	    in_file = this_in_file;
1718         } else {
1719             if (headers_are_equal(rval, this_result) &&
1720                 actual_file == this_actual_file &&
1721 		in_file == this_in_file) {
1722                 /* Do nothing. */
1723             } else {
1724                 success = FALSE;
1725             }
1726             free(this_result);
1727         }
1728     }
1729 
1730     g_ptr_array_free_full(ops);
1731 
1732     if (!success) {
1733         amfree(rval);
1734 	/* TODO: be more specific here */
1735 	device_set_error(dself,
1736                          g_strdup("One or more devices failed to seek_file"),
1737 	    DEVICE_STATUS_DEVICE_ERROR);
1738         return NULL;
1739     }
1740 
1741     /* update our state */
1742     g_mutex_lock(dself->device_mutex);
1743     dself->in_file = in_file;
1744     g_mutex_unlock(dself->device_mutex);
1745     dself->file = actual_file;
1746 
1747     return rval;
1748 }
1749 
1750 typedef struct {
1751     GenericOp base;
1752     guint64 block; /* IN */
1753 } SeekBlockOp;
1754 
1755 /* a GFunc. */
seek_block_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)1756 static void seek_block_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
1757     SeekBlockOp * op = data;
1758     op->base.result =
1759         GINT_TO_POINTER(device_seek_block(op->base.child, op->block));
1760 }
1761 
1762 static gboolean
rait_device_seek_block(Device * dself,guint64 block)1763 rait_device_seek_block (Device * dself, guint64 block) {
1764     GPtrArray * ops;
1765     guint i;
1766     gboolean success;
1767 
1768     RaitDevice * self = RAIT_DEVICE(dself);
1769 
1770     if (rait_device_in_error(self)) return FALSE;
1771 
1772     ops = g_ptr_array_sized_new(self->private->children->len);
1773     for (i = 0; i < self->private->children->len; i ++) {
1774         SeekBlockOp * op;
1775         if ((int)i == self->private->failed)
1776             continue; /* This device is broken. */
1777         op = g_new(SeekBlockOp, 1);
1778         op->base.child = g_ptr_array_index(self->private->children, i);
1779         op->base.child_index = i;
1780         op->block = block;
1781         g_ptr_array_add(ops, op);
1782     }
1783 
1784     do_rait_child_ops(self, seek_block_do_op, ops);
1785 
1786     success = g_ptr_array_union_robust(RAIT_DEVICE(self),
1787                                        ops, extract_boolean_generic_op);
1788 
1789     g_ptr_array_free_full(ops);
1790 
1791     if (!success) {
1792 	/* TODO: be more specific here */
1793 	device_set_error(dself,
1794 	    stralloc("One or more devices failed to seek_block"),
1795 	    DEVICE_STATUS_DEVICE_ERROR);
1796         return FALSE;
1797     }
1798 
1799     dself->block = block;
1800     return TRUE;
1801 }
1802 
1803 typedef struct {
1804     GenericOp base;
1805     gpointer buffer; /* IN */
1806     int read_size;      /* IN/OUT -- note not a pointer */
1807     int desired_read_size; /* bookkeeping */
1808 } ReadBlockOp;
1809 
1810 /* a GFunc. */
read_block_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)1811 static void read_block_do_op(gpointer data,
1812                              gpointer user_data G_GNUC_UNUSED) {
1813     ReadBlockOp * op = data;
1814     op->base.result =
1815         GINT_TO_POINTER(device_read_block(op->base.child, op->buffer,
1816                                           &(op->read_size)));
1817     if (op->read_size > op->desired_read_size) {
1818 	g_warning("child device %s tried to return an oversized block, which the RAIT device does not support",
1819 		  op->base.child->device_name);
1820     }
1821 }
1822 
1823 /* A BooleanExtractor. This one checks for a successful read. */
extract_boolean_read_block_op_data(gpointer data)1824 static gboolean extract_boolean_read_block_op_data(gpointer data) {
1825     ReadBlockOp * op = data;
1826     return GPOINTER_TO_INT(op->base.result) == op->desired_read_size;
1827 }
1828 
1829 /* A BooleanExtractor. This one checks for EOF. */
extract_boolean_read_block_op_eof(gpointer data)1830 static gboolean extract_boolean_read_block_op_eof(gpointer data) {
1831     ReadBlockOp * op = data;
1832     return op->base.child->is_eof;
1833 }
1834 
1835 /* Counts the number of elements in an array matching a given proposition. */
g_ptr_array_count(GPtrArray * array,BooleanExtractor filter)1836 static int g_ptr_array_count(GPtrArray * array, BooleanExtractor filter) {
1837     int rval;
1838     unsigned int i;
1839     rval = 0;
1840     for (i = 0; i < array->len ; i++) {
1841         if (filter(g_ptr_array_index(array, i)))
1842             rval ++;
1843     }
1844     return rval;
1845 }
1846 
raid_block_reconstruction(RaitDevice * self,GPtrArray * ops,gpointer buf,size_t bufsize)1847 static gboolean raid_block_reconstruction(RaitDevice * self, GPtrArray * ops,
1848                                       gpointer buf, size_t bufsize) {
1849     guint num_children, data_children;
1850     gsize blocksize;
1851     gsize child_blocksize;
1852     guint i;
1853     int parity_child;
1854     gpointer parity_block = NULL;
1855     gboolean success;
1856 
1857     success = TRUE;
1858 
1859     blocksize = DEVICE(self)->block_size;
1860     find_simple_params(self, &num_children, &data_children);
1861 
1862     if (num_children > 1)
1863         parity_child = num_children - 1;
1864     else
1865         parity_child = -1;
1866 
1867     child_blocksize = blocksize / data_children;
1868 
1869     for (i = 0; i < ops->len; i ++) {
1870         ReadBlockOp * op = g_ptr_array_index(ops, i);
1871         if (!extract_boolean_read_block_op_data(op))
1872             continue;
1873         if ((int)(op->base.child_index) == parity_child) {
1874             parity_block = op->buffer;
1875         } else {
1876 	    g_assert(child_blocksize * (op->base.child_index+1) <= bufsize);
1877             memcpy((char *)buf + child_blocksize * op->base.child_index, op->buffer,
1878                    child_blocksize);
1879         }
1880     }
1881     if (self->private->status == RAIT_STATUS_COMPLETE) {
1882 	g_assert(parity_block != NULL); /* should have found parity_child */
1883 
1884         if (num_children >= 2) {
1885             /* Verify the parity block. This code is inefficient but
1886                does the job for the 2-device case, too. */
1887             gpointer constructed_parity;
1888             GPtrArray * data_extents;
1889 
1890             constructed_parity = g_malloc(child_blocksize);
1891             data_extents = g_ptr_array_sized_new(data_children);
1892             for (i = 0; i < data_children; i ++) {
1893                 ReadBlockOp * op = g_ptr_array_index(ops, i);
1894                 g_assert(extract_boolean_read_block_op_data(op));
1895                 if ((int)op->base.child_index == parity_child)
1896                     continue;
1897                 g_ptr_array_add(data_extents, op->buffer);
1898             }
1899             make_parity_block_extents(data_extents, constructed_parity,
1900                                       child_blocksize);
1901 
1902             if (0 != memcmp(parity_block, constructed_parity,
1903                             child_blocksize)) {
1904                 device_set_error(DEVICE(self),
1905 		    stralloc(_("RAIT is inconsistent: Parity block did not match data blocks.")),
1906 		    DEVICE_STATUS_DEVICE_ERROR);
1907 		/* TODO: can't we just isolate the device in this case? */
1908                 success = FALSE;
1909             }
1910             g_ptr_array_free(data_extents, TRUE);
1911             amfree(constructed_parity);
1912         } else { /* do nothing. */ }
1913     } else if (self->private->status == RAIT_STATUS_DEGRADED) {
1914 	g_assert(self->private->failed >= 0 && self->private->failed < (int)num_children);
1915         /* We are in degraded mode. What's missing? */
1916         if (self->private->failed == parity_child) {
1917             /* do nothing. */
1918         } else if (num_children >= 2) {
1919             /* Reconstruct failed block from parity block. */
1920             GPtrArray * data_extents = g_ptr_array_new();
1921 
1922             for (i = 0; i < data_children; i ++) {
1923                 ReadBlockOp * op = g_ptr_array_index(ops, i);
1924                 if (!extract_boolean_read_block_op_data(op))
1925                     continue;
1926                 g_ptr_array_add(data_extents, op->buffer);
1927             }
1928 
1929             /* Conveniently, the reconstruction is the same procedure
1930                as the parity generation. This even works if there is
1931                only one remaining device! */
1932             make_parity_block_extents(data_extents,
1933                                       (char *)buf + (child_blocksize *
1934                                              self->private->failed),
1935                                       child_blocksize);
1936 
1937             /* The array members belong to our ops argument. */
1938             g_ptr_array_free(data_extents, TRUE);
1939         } else {
1940             g_assert_not_reached();
1941         }
1942     } else {
1943 	/* device is already in FAILED state -- we shouldn't even be here */
1944         success = FALSE;
1945     }
1946     return success;
1947 }
1948 
1949 static int
rait_device_read_block(Device * dself,gpointer buf,int * size)1950 rait_device_read_block (Device * dself, gpointer buf, int * size) {
1951     GPtrArray * ops;
1952     guint i;
1953     gboolean success;
1954     guint num_children, data_children;
1955     gsize blocksize = dself->block_size;
1956     gsize child_blocksize;
1957 
1958     RaitDevice * self = RAIT_DEVICE(dself);
1959 
1960     if (rait_device_in_error(self)) return -1;
1961 
1962     find_simple_params(self, &num_children, &data_children);
1963 
1964     /* tell caller they haven't given us a big enough buffer */
1965     if (blocksize > (gsize)*size) {
1966 	g_assert(blocksize < INT_MAX);
1967 	*size = (int)blocksize;
1968 	return 0;
1969     }
1970 
1971     g_assert(blocksize % data_children == 0); /* see find_block_size */
1972     child_blocksize = blocksize / data_children;
1973 
1974     ops = g_ptr_array_sized_new(num_children);
1975     for (i = 0; i < num_children; i ++) {
1976         ReadBlockOp * op;
1977         if ((int)i == self->private->failed)
1978             continue; /* This device is broken. */
1979         op = g_new(ReadBlockOp, 1);
1980         op->base.child = g_ptr_array_index(self->private->children, i);
1981         op->base.child_index = i;
1982         op->buffer = g_malloc(child_blocksize);
1983         op->desired_read_size = op->read_size = child_blocksize;
1984         g_ptr_array_add(ops, op);
1985     }
1986 
1987     do_rait_child_ops(self, read_block_do_op, ops);
1988 
1989     if (g_ptr_array_count(ops, extract_boolean_read_block_op_data)) {
1990         if (!g_ptr_array_union_robust(RAIT_DEVICE(self),
1991                                      ops,
1992                                      extract_boolean_read_block_op_data)) {
1993 	    /* TODO: be more specific */
1994 	    device_set_error(dself,
1995 		stralloc(_("Error occurred combining blocks from child devices")),
1996 		DEVICE_STATUS_DEVICE_ERROR);
1997 	    success = FALSE;
1998 	} else {
1999 	    /* raid_block_reconstruction sets the error status if necessary */
2000 	    success = raid_block_reconstruction(RAIT_DEVICE(self),
2001                                                 ops, buf, (size_t)*size);
2002 	}
2003     } else {
2004         success = FALSE;
2005         if (g_ptr_array_union_robust(RAIT_DEVICE(self),
2006                                      ops,
2007                                      extract_boolean_read_block_op_eof)) {
2008 	    device_set_error(dself,
2009 		stralloc(_("EOF")),
2010 		DEVICE_STATUS_SUCCESS);
2011             dself->is_eof = TRUE;
2012 	    g_mutex_lock(dself->device_mutex);
2013 	    dself->in_file = FALSE;
2014 	    g_mutex_unlock(dself->device_mutex);
2015         } else {
2016 	    device_set_error(dself,
2017 		stralloc(_("All child devices failed to read, but not all are at eof")),
2018 		DEVICE_STATUS_DEVICE_ERROR);
2019 	}
2020     }
2021 
2022     for (i = 0; i < ops->len; i ++) {
2023         ReadBlockOp * op = g_ptr_array_index(ops, i);
2024         amfree(op->buffer);
2025     }
2026     g_ptr_array_free_full(ops);
2027 
2028     if (success) {
2029 	dself->block++;
2030 	*size = blocksize;
2031 	g_mutex_lock(dself->device_mutex);
2032 	dself->bytes_read += blocksize;
2033 	g_mutex_unlock(dself->device_mutex);
2034         return blocksize;
2035     } else {
2036         return -1;
2037     }
2038 }
2039 
2040 /* property utility functions */
2041 
2042 typedef struct {
2043     GenericOp base;
2044     DevicePropertyId id;   /* IN */
2045     GValue value;          /* IN/OUT */
2046     PropertySurety surety; /* IN (for set) */
2047     PropertySource source; /* IN (for set) */
2048 } PropertyOp;
2049 
2050 /* Creates a GPtrArray of PropertyOf for a get or set operation. */
make_property_op_array(RaitDevice * self,DevicePropertyId id,GValue * value,PropertySurety surety,PropertySource source)2051 static GPtrArray * make_property_op_array(RaitDevice * self,
2052                                           DevicePropertyId id,
2053                                           GValue * value,
2054 					  PropertySurety surety,
2055 					  PropertySource source) {
2056     guint i;
2057     GPtrArray * ops;
2058     ops = g_ptr_array_sized_new(self->private->children->len);
2059     for (i = 0; i < self->private->children->len; i ++) {
2060         PropertyOp * op;
2061 
2062         if ((signed)i == self->private->failed) {
2063             continue;
2064         }
2065 
2066         op = g_new(PropertyOp, 1);
2067         op->base.child = g_ptr_array_index(self->private->children, i);
2068         op->id = id;
2069         bzero(&(op->value), sizeof(op->value));
2070         if (value != NULL) {
2071             g_value_unset_copy(value, &(op->value));
2072         }
2073 	op->surety = surety;
2074 	op->source = source;
2075         g_ptr_array_add(ops, op);
2076     }
2077 
2078     return ops;
2079 }
2080 
2081 /* A GFunc. */
property_get_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)2082 static void property_get_do_op(gpointer data,
2083                                gpointer user_data G_GNUC_UNUSED) {
2084     PropertyOp * op = data;
2085 
2086     bzero(&(op->value), sizeof(op->value));
2087     op->base.result =
2088         GINT_TO_POINTER(device_property_get(op->base.child, op->id,
2089                                             &(op->value)));
2090 }
2091 
2092 /* A GFunc. */
property_set_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)2093 static void property_set_do_op(gpointer data,
2094                                gpointer user_data G_GNUC_UNUSED) {
2095     PropertyOp * op = data;
2096 
2097     op->base.result =
2098         GINT_TO_POINTER(device_property_set_ex(op->base.child, op->id,
2099 					       &(op->value), op->surety,
2100 					       op->source));
2101     g_value_unset(&(op->value));
2102 }
2103 
2104 /* PropertyGetFns and PropertySetFns */
2105 
2106 static gboolean
property_get_block_size_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety * surety,PropertySource * source)2107 property_get_block_size_fn(Device *dself,
2108     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2109     PropertySurety *surety, PropertySource *source)
2110 {
2111     RaitDevice *self = RAIT_DEVICE(dself);
2112     gsize my_block_size;
2113 
2114     if (dself->block_size_source != PROPERTY_SOURCE_DEFAULT) {
2115 	my_block_size = dself->block_size;
2116 
2117 	if (surety)
2118 	    *surety = dself->block_size_surety;
2119     } else {
2120 	gsize child_block_size;
2121 	child_block_size = calculate_block_size_from_children(self,
2122 						    &my_block_size);
2123 	if (child_block_size == 0)
2124 	    return FALSE;
2125 
2126 	if (surety)
2127 	    *surety = PROPERTY_SURETY_BAD; /* may still change */
2128     }
2129 
2130     if (val) {
2131 	g_value_unset_init(val, G_TYPE_INT);
2132 	g_assert(my_block_size < G_MAXINT); /* gsize -> gint */
2133 	g_value_set_int(val, (gint)my_block_size);
2134     }
2135 
2136     if (source)
2137 	*source = dself->block_size_source;
2138 
2139     return TRUE;
2140 }
2141 
2142 static gboolean
property_set_block_size_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety surety,PropertySource source)2143 property_set_block_size_fn(Device *dself,
2144     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2145     PropertySurety surety, PropertySource source)
2146 {
2147     RaitDevice *self = RAIT_DEVICE(dself);
2148     gint my_block_size = g_value_get_int(val);
2149     guint data_children;
2150 
2151     find_simple_params(self, NULL, &data_children);
2152     if ((my_block_size % data_children) != 0) {
2153 	device_set_error(dself,
2154 	    vstrallocf(_("Block size must be a multiple of %d"), data_children),
2155 	    DEVICE_STATUS_DEVICE_ERROR);
2156 	return FALSE;
2157     }
2158 
2159     dself->block_size = my_block_size;
2160     dself->block_size_source = source;
2161     dself->block_size_surety = surety;
2162 
2163     if (!fix_block_size(self))
2164 	return FALSE;
2165 
2166     return TRUE;
2167 }
2168 
2169 static gboolean
property_get_canonical_name_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety * surety,PropertySource * source)2170 property_get_canonical_name_fn(Device *dself,
2171     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2172     PropertySurety *surety, PropertySource *source)
2173 {
2174     RaitDevice *self = RAIT_DEVICE(dself);
2175     char *canonical = child_device_names_to_rait_name(self);
2176 
2177     if (val) {
2178 	g_value_unset_init(val, G_TYPE_STRING);
2179 	g_value_set_string(val, canonical);
2180 	g_free(canonical);
2181     }
2182 
2183     if (surety)
2184 	*surety = PROPERTY_SURETY_GOOD;
2185 
2186     if (source)
2187 	*source = PROPERTY_SOURCE_DETECTED;
2188 
2189     return TRUE;
2190 }
2191 
2192 static gboolean
property_get_concurrency_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety * surety,PropertySource * source)2193 property_get_concurrency_fn(Device *dself,
2194     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2195     PropertySurety *surety, PropertySource *source)
2196 {
2197     RaitDevice *self = RAIT_DEVICE(dself);
2198     ConcurrencyParadigm result;
2199     guint i;
2200     GPtrArray * ops;
2201     gboolean success;
2202 
2203     ops = make_property_op_array(self, PROPERTY_CONCURRENCY, NULL, 0, 0);
2204     do_rait_child_ops(self, property_get_do_op, ops);
2205 
2206     /* find the most restrictive paradigm acceptable to all
2207      * child devices */
2208     result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
2209     success = TRUE;
2210     for (i = 0; i < ops->len; i ++) {
2211         ConcurrencyParadigm cur;
2212         PropertyOp * op = g_ptr_array_index(ops, i);
2213 
2214         if (!op->base.result
2215 	    || G_VALUE_TYPE(&(op->value)) != CONCURRENCY_PARADIGM_TYPE) {
2216 	    success = FALSE;
2217 	    break;
2218 	}
2219 
2220         cur = g_value_get_enum(&(op->value));
2221         if (result == CONCURRENCY_PARADIGM_EXCLUSIVE ||
2222             cur == CONCURRENCY_PARADIGM_EXCLUSIVE) {
2223             result = CONCURRENCY_PARADIGM_EXCLUSIVE;
2224         } else if (result == CONCURRENCY_PARADIGM_SHARED_READ ||
2225                    cur == CONCURRENCY_PARADIGM_SHARED_READ) {
2226             result = CONCURRENCY_PARADIGM_SHARED_READ;
2227         } else if (result == CONCURRENCY_PARADIGM_RANDOM_ACCESS &&
2228                    cur == CONCURRENCY_PARADIGM_RANDOM_ACCESS) {
2229             result = CONCURRENCY_PARADIGM_RANDOM_ACCESS;
2230         } else {
2231             success = FALSE;
2232 	    break;
2233         }
2234     }
2235 
2236     g_ptr_array_free_full(ops);
2237 
2238     if (success) {
2239 	if (val) {
2240 	    g_value_unset_init(val, CONCURRENCY_PARADIGM_TYPE);
2241 	    g_value_set_enum(val, result);
2242 	}
2243 
2244 	if (surety)
2245 	    *surety = PROPERTY_SURETY_GOOD;
2246 
2247 	if (source)
2248 	    *source = PROPERTY_SOURCE_DETECTED;
2249     }
2250 
2251     return success;
2252 }
2253 
2254 static gboolean
property_get_streaming_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety * surety,PropertySource * source)2255 property_get_streaming_fn(Device *dself,
2256     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2257     PropertySurety *surety, PropertySource *source)
2258 {
2259     RaitDevice *self = RAIT_DEVICE(dself);
2260     StreamingRequirement result;
2261     guint i;
2262     GPtrArray * ops;
2263     gboolean success;
2264 
2265     ops = make_property_op_array(self, PROPERTY_STREAMING, NULL, 0, 0);
2266     do_rait_child_ops(self, property_get_do_op, ops);
2267 
2268     /* combine the child streaming requirements, selecting the strongest
2269      * requirement of the bunch. */
2270     result = STREAMING_REQUIREMENT_NONE;
2271     success = TRUE;
2272     for (i = 0; i < ops->len; i ++) {
2273         StreamingRequirement cur;
2274         PropertyOp * op = g_ptr_array_index(ops, i);
2275 
2276         if (!op->base.result
2277 	    || G_VALUE_TYPE(&(op->value)) != STREAMING_REQUIREMENT_TYPE) {
2278 	    success = FALSE;
2279 	    break;
2280 	}
2281 
2282         cur = g_value_get_enum(&(op->value));
2283         if (result == STREAMING_REQUIREMENT_REQUIRED ||
2284             cur == STREAMING_REQUIREMENT_REQUIRED) {
2285             result = STREAMING_REQUIREMENT_REQUIRED;
2286         } else if (result == STREAMING_REQUIREMENT_DESIRED ||
2287                    cur == STREAMING_REQUIREMENT_DESIRED) {
2288             result = STREAMING_REQUIREMENT_DESIRED;
2289         } else if (result == STREAMING_REQUIREMENT_NONE &&
2290                    cur == STREAMING_REQUIREMENT_NONE) {
2291             result = STREAMING_REQUIREMENT_NONE;
2292         } else {
2293             success = FALSE;
2294 	    break;
2295         }
2296     }
2297 
2298     g_ptr_array_free_full(ops);
2299 
2300     if (success) {
2301 	if (val) {
2302 	    g_value_unset_init(val, STREAMING_REQUIREMENT_TYPE);
2303 	    g_value_set_enum(val, result);
2304 	}
2305 
2306 	if (surety)
2307 	    *surety = PROPERTY_SURETY_GOOD;
2308 
2309 	if (source)
2310 	    *source = PROPERTY_SOURCE_DETECTED;
2311     }
2312 
2313     return success;
2314 }
2315 
2316 static gboolean
property_get_boolean_and_fn(Device * dself,DevicePropertyBase * base,GValue * val,PropertySurety * surety,PropertySource * source)2317 property_get_boolean_and_fn(Device *dself,
2318     DevicePropertyBase *base, GValue *val,
2319     PropertySurety *surety, PropertySource *source)
2320 {
2321     RaitDevice *self = RAIT_DEVICE(dself);
2322     gboolean result;
2323     guint i;
2324     GPtrArray * ops;
2325     gboolean success;
2326 
2327     ops = make_property_op_array(self, base->ID, NULL, 0, 0);
2328     do_rait_child_ops(self, property_get_do_op, ops);
2329 
2330     /* combine the child values, applying a simple AND */
2331     result = TRUE;
2332     success = TRUE;
2333     for (i = 0; i < ops->len; i ++) {
2334         PropertyOp * op = g_ptr_array_index(ops, i);
2335 
2336         if (!op->base.result || !G_VALUE_HOLDS_BOOLEAN(&(op->value))) {
2337 	    success = FALSE;
2338 	    break;
2339 	}
2340 
2341 	if (!g_value_get_boolean(&(op->value))) {
2342 	    result = FALSE;
2343 	    break;
2344 	}
2345     }
2346 
2347     g_ptr_array_free_full(ops);
2348 
2349     if (success) {
2350 	if (val) {
2351 	    g_value_unset_init(val, G_TYPE_BOOLEAN);
2352 	    g_value_set_boolean(val, result);
2353 	}
2354 
2355 	if (surety)
2356 	    *surety = PROPERTY_SURETY_GOOD;
2357 
2358 	if (source)
2359 	    *source = PROPERTY_SOURCE_DETECTED;
2360     }
2361 
2362     return success;
2363 }
2364 
2365 static gboolean
property_get_medium_access_type_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety * surety,PropertySource * source)2366 property_get_medium_access_type_fn(Device *dself,
2367     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2368     PropertySurety *surety, PropertySource *source)
2369 {
2370     RaitDevice *self = RAIT_DEVICE(dself);
2371     MediaAccessMode result;
2372     guint i;
2373     GPtrArray * ops;
2374     gboolean success;
2375 
2376     ops = make_property_op_array(self, PROPERTY_MEDIUM_ACCESS_TYPE, NULL, 0, 0);
2377     do_rait_child_ops(self, property_get_do_op, ops);
2378 
2379     /* combine the modes as best we can */
2380     result = 0;
2381     success = TRUE;
2382     for (i = 0; i < ops->len; i ++) {
2383         MediaAccessMode cur;
2384         PropertyOp * op = g_ptr_array_index(ops, i);
2385 
2386         if (!op->base.result || G_VALUE_TYPE(&(op->value)) != MEDIA_ACCESS_MODE_TYPE) {
2387 	    success = FALSE;
2388 	    break;
2389 	}
2390 
2391         cur = g_value_get_enum(&(op->value));
2392 
2393 	if (i == 0) {
2394 	    result = cur;
2395 	} else if ((result == MEDIA_ACCESS_MODE_READ_ONLY &&
2396 		    cur == MEDIA_ACCESS_MODE_WRITE_ONLY) ||
2397 		   (result == MEDIA_ACCESS_MODE_WRITE_ONLY &&
2398 		    cur == MEDIA_ACCESS_MODE_READ_ONLY)) {
2399 	    /* Invalid combination; one device can only read, other
2400 	       can only write. */
2401 	    success = FALSE;
2402 	    break;
2403 	} else if (result == MEDIA_ACCESS_MODE_READ_ONLY ||
2404 		   cur == MEDIA_ACCESS_MODE_READ_ONLY) {
2405 	    result = MEDIA_ACCESS_MODE_READ_ONLY;
2406 	} else if (result == MEDIA_ACCESS_MODE_WRITE_ONLY ||
2407 		   cur == MEDIA_ACCESS_MODE_WRITE_ONLY) {
2408 	    result = MEDIA_ACCESS_MODE_WRITE_ONLY;
2409 	} else if (result == MEDIA_ACCESS_MODE_WORM ||
2410 		   cur == MEDIA_ACCESS_MODE_WORM) {
2411 	    result = MEDIA_ACCESS_MODE_WORM;
2412 	} else if (result == MEDIA_ACCESS_MODE_READ_WRITE &&
2413 		   cur == MEDIA_ACCESS_MODE_READ_WRITE) {
2414 	    result = MEDIA_ACCESS_MODE_READ_WRITE;
2415 	} else {
2416 	    success = FALSE;
2417 	    break;
2418 	}
2419     }
2420 
2421     g_ptr_array_free_full(ops);
2422 
2423     if (success) {
2424 	if (val) {
2425 	    g_value_unset_init(val, MEDIA_ACCESS_MODE_TYPE);
2426 	    g_value_set_enum(val, result);
2427 	}
2428 
2429 	if (surety)
2430 	    *surety = PROPERTY_SURETY_GOOD;
2431 
2432 	if (source)
2433 	    *source = PROPERTY_SOURCE_DETECTED;
2434     }
2435 
2436     return success;
2437 }
2438 
2439 static gboolean
property_get_max_volume_usage_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety * surety,PropertySource * source)2440 property_get_max_volume_usage_fn(Device *dself,
2441     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2442     PropertySurety *surety, PropertySource *source)
2443 {
2444     RaitDevice *self = RAIT_DEVICE(dself);
2445     guint64 result;
2446     guint i;
2447     GPtrArray * ops;
2448     guint data_children;
2449 
2450     ops = make_property_op_array(self, PROPERTY_MAX_VOLUME_USAGE, NULL, 0, 0);
2451     do_rait_child_ops(self, property_get_do_op, ops);
2452 
2453     /* look for the smallest value that is set */
2454     result = 0;
2455     for (i = 0; i < ops->len; i ++) {
2456         guint64 cur;
2457         PropertyOp * op = g_ptr_array_index(ops, i);
2458 
2459         if (!op->base.result || !G_VALUE_HOLDS_UINT64(&(op->value))) {
2460 	    continue; /* ignore children without this property */
2461 	}
2462 
2463         cur = g_value_get_uint64(&(op->value));
2464 
2465 	if (!result || (cur && cur < result)) {
2466 	    result = cur;
2467 	}
2468     }
2469 
2470     g_ptr_array_free_full(ops);
2471 
2472     if (result) {
2473 	/* result contains the minimum usage on any child.  We can use that space
2474 	 * on each of our data children, so the total is larger */
2475 	find_simple_params(self, NULL, &data_children);
2476 	result *= data_children;
2477 
2478 	if (val) {
2479 	    g_value_unset_init(val, G_TYPE_UINT64);
2480 	    g_value_set_uint64(val, result);
2481 	}
2482 
2483 	if (surety)
2484 	    *surety = PROPERTY_SURETY_GOOD;
2485 
2486 	if (source)
2487 	    *source = PROPERTY_SOURCE_DETECTED;
2488 
2489 	return TRUE;
2490     } else {
2491 	/* no result from any children, so we effectively don't have this property */
2492 	return FALSE;
2493     }
2494 }
2495 
2496 static gboolean
property_set_max_volume_usage_fn(Device * dself,DevicePropertyBase * base G_GNUC_UNUSED,GValue * val,PropertySurety surety,PropertySource source)2497 property_set_max_volume_usage_fn(Device *dself,
2498     DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
2499     PropertySurety surety, PropertySource source)
2500 {
2501     RaitDevice *self = RAIT_DEVICE(dself);
2502     guint64 parent_usage;
2503     guint64 child_usage;
2504     GValue child_val;
2505     guint i;
2506     gboolean success;
2507     GPtrArray * ops;
2508     guint data_children;
2509 
2510     parent_usage = g_value_get_uint64(val);
2511     find_simple_params(self, NULL, &data_children);
2512 
2513     child_usage = parent_usage / data_children;
2514 
2515     bzero(&child_val, sizeof(child_val));
2516     g_value_init(&child_val, G_TYPE_UINT64);
2517     g_value_set_uint64(&child_val, child_usage);
2518 
2519     ops = make_property_op_array(self, PROPERTY_MAX_VOLUME_USAGE,
2520 				&child_val, surety, source);
2521     do_rait_child_ops(self, property_set_do_op, ops);
2522 
2523     /* if any of the kids succeeded, then we did too */
2524     success = FALSE;
2525     for (i = 0; i < ops->len; i ++) {
2526         PropertyOp * op = g_ptr_array_index(ops, i);
2527 
2528         if (op->base.result) {
2529 	    success = TRUE;
2530 	    break;
2531 	}
2532     }
2533 
2534     g_ptr_array_free_full(ops);
2535 
2536     return success;
2537 }
2538 
2539 typedef struct {
2540     GenericOp base;
2541     guint filenum;
2542 } RecycleFileOp;
2543 
2544 /* A GFunc */
recycle_file_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)2545 static void recycle_file_do_op(gpointer data,
2546                                gpointer user_data G_GNUC_UNUSED) {
2547     RecycleFileOp * op = data;
2548     op->base.result =
2549         GINT_TO_POINTER(device_recycle_file(op->base.child, op->filenum));
2550 }
2551 
2552 static gboolean
rait_device_recycle_file(Device * dself,guint filenum)2553 rait_device_recycle_file (Device * dself, guint filenum) {
2554     GPtrArray * ops;
2555     guint i;
2556     gboolean success;
2557 
2558     RaitDevice * self = RAIT_DEVICE(dself);
2559 
2560     if (rait_device_in_error(self)) return FALSE;
2561 
2562     ops = g_ptr_array_sized_new(self->private->children->len);
2563     for (i = 0; i < self->private->children->len; i ++) {
2564         RecycleFileOp * op;
2565         op = g_new(RecycleFileOp, 1);
2566         op->base.child = g_ptr_array_index(self->private->children, i);
2567         op->filenum = filenum;
2568         g_ptr_array_add(ops, op);
2569     }
2570 
2571     do_rait_child_ops(self, recycle_file_do_op, ops);
2572 
2573     success = g_ptr_array_and(ops, extract_boolean_generic_op);
2574 
2575     g_ptr_array_free_full(ops);
2576 
2577     if (!success) {
2578 	/* TODO: be more specific here */
2579 	device_set_error(dself,
2580 	    stralloc(_("One or more devices failed to recycle_file")),
2581 	    DEVICE_STATUS_DEVICE_ERROR);
2582         return FALSE;
2583     }
2584     return TRUE;
2585 }
2586 
2587 /* GFunc */
finish_do_op(gpointer data,gpointer user_data G_GNUC_UNUSED)2588 static void finish_do_op(gpointer data, gpointer user_data G_GNUC_UNUSED) {
2589     GenericOp * op = data;
2590     op->result = GINT_TO_POINTER(device_finish(op->child));
2591 }
2592 
2593 static gboolean
rait_device_finish(Device * self)2594 rait_device_finish (Device * self) {
2595     GPtrArray * ops;
2596     gboolean success;
2597     gboolean rval = TRUE;
2598 
2599     rval = !rait_device_in_error(self);
2600 
2601     ops = make_generic_boolean_op_array(RAIT_DEVICE(self));
2602 
2603     do_rait_child_ops(RAIT_DEVICE(self), finish_do_op, ops);
2604 
2605     success = g_ptr_array_and(ops, extract_boolean_generic_op);
2606     if (!success)
2607 	rval = FALSE;
2608 
2609     g_ptr_array_free_full(ops);
2610 
2611     self->access_mode = ACCESS_NULL;
2612 
2613     return rval;
2614 }
2615 
2616 static Device *
rait_device_factory(char * device_name,char * device_type,char * device_node)2617 rait_device_factory (char * device_name, char * device_type, char * device_node) {
2618     Device * rval;
2619     g_assert(0 == strcmp(device_type, "rait"));
2620     rval = DEVICE(g_object_new(TYPE_RAIT_DEVICE, NULL));
2621     device_open_device(rval, device_name, device_type, device_node);
2622     return rval;
2623 }
2624 
2625 void
rait_device_register(void)2626 rait_device_register (void) {
2627     static const char * device_prefix_list[] = {"rait", NULL};
2628     register_device(rait_device_factory, device_prefix_list);
2629 }
2630