Add externs to avoid multiple definitions, and then add missing definitions.
[rrq/maintain_lilo.git] / src / raid.c
1 /* raid.c  -  The RAID-1 hooks for LILO
2  * 
3  * Copyright 2001-2005 John Coffman
4  * Copyright 2009-2011 Joachim Wiedorn
5  * All rights reserved.
6  * 
7  * Licensed under the terms contained in the file 'COPYING'
8  * in the source directory.
9  */
10
11 #define _GNU_SOURCE
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <sys/stat.h>
20
21 /*#include <asm/page.h>*/
22
23 #include "config.h"
24 #include "lilo.h"
25 #include "common.h"
26 #include "raid.h"
27 #include "boot.h"
28 #include "device.h"
29 #include "geometry.h"
30 #include "bsect.h"
31 #include "cfg.h"
32 #include "partition.h"
33 #include "md-int.h"
34
35 static int lowest;
36 static DT_ENTRY *md_disk;
37 static DT_ENTRY *disk;
38 static unsigned int raid_base, raid_offset[MAX_RAID];
39 static char *raid_mbr[MAX_RAID];
40 static int raid_device[MAX_RAID+1];
41 static int raid_bios[MAX_RAID+1];
42 static int device;
43 enum {MD_NULL=0, MD_PARALLEL, MD_MIXED, MD_SKEWED};
44 int do_md_install, ndisk, md_bios;
45 static char *raid_list[MAX_RAID];
46 static int list_index[MAX_RAID];
47 static int nlist, faulty;
48 static int autocount;
49 static char *boot;
50
51 #define IS_COVERED    0x1000
52 #define TO_BE_COVERED 0x2000
53 #define COVERED   (IS_COVERED|TO_BE_COVERED)
54
55 static int is_primary(int device)
56 {
57     int mask;
58     
59     mask = has_partitions(device);
60     if (!mask) die("is_primary:  Not a valid device  0x%04X", device);
61     mask = device & ~mask;
62     return (mask && mask<=PART_MAX);
63 }
64
65
66
67 static int master(int device)
68 {
69     int mask;
70     
71     if (MAJOR(device) == MAJOR_FD) return device;
72     
73     mask = has_partitions(device);
74     if (!mask) die("master:  Not a valid device  0x%04X", device);
75     return device & mask;
76 }
77
78
79 static int is_accessible(int device)
80 {
81     int mask;
82     
83     mask = has_partitions(device);
84     if (!mask) die("is_accessible:  Not a valid device  0x%04X", device);
85     mask = device & ~mask;
86     return (mask<=PART_MAX);
87 }
88
89
90 int raid_setup(void)
91 {
92     int pass, mask;
93     struct stat st;
94     int md_fd;
95     struct md_version md_version_info;
96     md_disk_info_t md_disk_info;
97     md_array_info_t md_array_info;
98     GEOMETRY geo;
99     DEVICE dev;
100     char *extrap;
101     int raid_offset_set, all_pri_eq, pri_index;
102     int pri_offset;
103     int raid_limit;
104     
105     if (!(boot=cfg_get_strg(cf_options,"boot"))) {
106         boot = "/";
107 #if 0
108       warn("RAID1 install implied by omitted 'boot='");
109 #endif
110     }
111     if (stat(boot,&st)<0) die("raid_setup: stat(\"%s\")", boot);
112
113     if (verbose>=5)
114         printf("raid_setup: dev=%04X  rdev=%04X\n",
115                                 (int)st.st_dev, (int)st.st_rdev);
116
117 #if BETA_TEST
118         fflush(stdout);
119 #endif
120
121     if ( MAJOR(st.st_rdev) != MAJOR_MD ) {      /* not raid */
122         if (cfg_get_strg(cf_options, RAID_EXTRA_BOOT))
123             die("Not a RAID install, '" RAID_EXTRA_BOOT "=' not allowed");
124         return 0;
125     }
126     else {      /* It is a RAID installation */
127
128         if (!nowarn && boot[0]=='/' && !boot[1])
129            warn("RAID1 install implied by 'boot=/'\n");
130            
131 /* scan the devices in /proc/partitions */
132         pf_hard_disk_scan();
133
134
135         if ((md_fd=dev_open(&dev, st.st_rdev, O_NOACCESS) ) < 0)
136             die("Unable to open %s",boot);
137         boot = stralloc(dev.name);
138         if (fstat(md_fd,&st) < 0)
139             die("Unable to stat %s",boot);
140         if (!S_ISBLK(st.st_mode))
141             die("%s is not a block device",boot);
142
143         boot_dev_nr = st.st_rdev;       /* set this very early */
144
145         if (ioctl(md_fd,RAID_VERSION,&md_version_info) < 0)
146             die("Unable to get RAID version on %s", boot);
147         if (verbose >= 4) printf("RAID_VERSION = %d.%d\n",
148                 (int)md_version_info.major,
149                 (int)md_version_info.minor);
150         if (md_version_info.major > 0)
151             die("Raid major versions > 0 are not supported");
152         if (md_version_info.minor < 90)
153             die("Raid versions < 0.90 are not supported");
154         
155         if (ioctl(md_fd,GET_ARRAY_INFO,&md_array_info) < 0)
156             die("Unable to get RAID info on %s",boot);
157         if (verbose >= 4) printf("GET_ARRAY_INFO version = %d.%d\n",
158                 (int)md_array_info.major_version,
159                 (int)md_array_info.minor_version);
160         if (md_version_info.major != 0 || md_version_info.minor != 90 ||
161             ((md_array_info.major_version != 0 ||
162                 md_array_info.minor_version != 90) &&
163              (md_array_info.major_version != 1 ||
164                 md_array_info.minor_version != 0))
165             ) {
166             die("Incompatible Raid version information on %s   (RV=%d.%d GAI=%d.%d)",
167                 boot,
168                 (int)md_version_info.major,
169                 (int)md_version_info.minor,
170                 (int)md_array_info.major_version,
171                 (int)md_array_info.minor_version);
172             }
173         if (md_array_info.level != 1)
174             die("Only RAID1 devices are supported as boot devices");
175         if (!linear && !lba32) {
176             lba32 = 1;
177             if (!nowarn)
178                 warn("RAID install requires LBA32 or LINEAR;"
179                         " LBA32 assumed.\n");
180         }
181         extrap = cfg_get_strg(cf_options, RAID_EXTRA_BOOT);
182         extra = !extrap ? X_AUTO :
183                 !strcasecmp(extrap,"none") ? X_NONE :
184                 !strcasecmp(extrap,"auto") ? X_AUTO :
185                 !strcasecmp(extrap,"mbr-only") ? X_MBR_ONLY :
186                 !strcasecmp(extrap,"mbr") ? X_MBR :
187                     X_SPEC;
188         
189         do_md_install = MD_PARALLEL;
190
191         all_pri_eq = 1;
192         raid_offset_set = pri_index = pri_offset = 0;
193         raid_flags = FLAG_RAID;
194         md_bios = 0xFF;                 /* we want to find the minimum */
195         ndisk = 0;                      /* count the number of disks on-line */
196         nlist = 0;
197         faulty = 0;
198         
199         device = MKDEV(MD_MAJOR, md_array_info.md_minor);
200         
201     /* search the disk table for a definition */
202         md_disk = disktab;
203         while (md_disk && md_disk->device != device)
204             md_disk = md_disk->next;
205             
206         if (!md_disk) {
207             md_disk = alloc_t(DT_ENTRY);
208             md_disk->device = MKDEV(MD_MAJOR, md_array_info.md_minor);
209             md_disk->bios = -1; /* use the default */
210             md_disk->next = disktab;
211             disktab = md_disk;
212         }
213
214         if (verbose >= 2) {
215            printf("RAID info:  nr=%d, raid=%d, active=%d, working=%d, failed=%d, spare=%d\n",
216                 md_array_info.nr_disks,
217                 md_array_info.raid_disks,
218                 md_array_info.active_disks,
219                 md_array_info.working_disks,
220                 md_array_info.failed_disks,
221                 md_array_info.spare_disks );
222         }
223
224     /* scan through all the RAID devices */
225         raid_limit = md_array_info.raid_disks;
226         if (md_array_info.active_disks < md_array_info.raid_disks) {
227             if (!force_raid) die("Not all RAID-1 disks are active; use '-H' to install to active disks only");
228             else {
229                 warn("Partial RAID-1 install on active disks only; booting is not failsafe\n");
230                 raid_limit = md_array_info.raid_disks;
231             }
232         }
233         raid_index = 0;
234         for (pass=0; pass < raid_limit; pass++) {
235             DEVICE dev;
236             int disk_fd;
237             char new_name[MAX_TOKEN+1];
238             char *np;
239             
240             md_disk_info.number = pass;
241             if (ioctl(md_fd,GET_DISK_INFO,&md_disk_info) < 0)
242 #if 1
243                 die("raid: GET_DISK_INFO: %s, pass=%d", strerror(errno), pass);
244 #else
245                 {
246                 printf("raid: GET_DISK_INFO: %s, pass=%d\n", strerror(errno), pass);
247                 continue;
248                 }
249 #endif
250             device = MKDEV(md_disk_info.major, md_disk_info.minor);
251             if(verbose>=3) printf("md: RAIDset device %d = 0x%04X\n", pass, device);        
252             if (device == 0) { /* empty slot left over from recovery process */
253                 faulty++;
254                 warn("Faulty disk in RAID-1 array; boot with caution!!");
255                 continue;
256             }
257             disk_fd = dev_open(&dev,device,O_NOACCESS);
258             if (md_disk_info.state & (1 << MD_DISK_FAULTY)) {
259                 printf("disk %s marked as faulty, skipping\n",dev.name);
260                 faulty++;
261                 continue;
262             }
263             geo_get(&geo, device, -1, 1);
264             disk = alloc_t(DT_ENTRY);
265             if (verbose>=3)
266                 printf("RAID scan: geo_get: returns geo->device = 0x%02X"
267                       " for device %04X\n", geo.device, device);
268               
269             disk->bios = geo.device;    /* will be overwritten */
270             disk->device = device;
271               /* used to mask above with 0xFFF0; forces MBR; sloppy, mask may be: 0xFFF8 */
272             disk->sectors = geo.sectors;
273             disk->heads = geo.heads;
274             disk->cylinders = geo.cylinders;
275             disk->start = geo.start;
276             if (ndisk==0) {
277                 raid_base = geo.start;
278                 raid_index = pass;
279             }
280             raid_offset[ndisk] = geo.start - raid_base;
281             raid_device[ndisk] = device;
282
283             if (raid_offset[ndisk]) {
284                 do_md_install = MD_SKEWED;       /* flag non-zero raid_offset */
285             }
286
287             if (all_pri_eq && is_primary(device)) {
288                 if (raid_offset_set) {
289                     all_pri_eq &= (pri_offset == raid_offset[ndisk]);
290                 } else {
291                     pri_offset = raid_offset[ndisk];
292                     raid_offset_set = 1;
293                     pri_index = ndisk;
294                 }
295             }
296
297 #if 1
298             if (geo.device < md_bios) { /* OLD: use smallest device code */
299 #else
300             if (ndisk==0) {     /* NEW: use the device code of the first device */
301 #endif
302                 md_bios = geo.device;   /* find smallest device code, period */
303                 lowest = ndisk;         /* record where */
304             }
305             raid_bios[ndisk] = geo.device;  /* record device code */
306
307             disk->next = disktab;
308             disktab = disk;
309
310             if (verbose >= 3 && do_md_install) {
311                 printf("disk->start = %d\t\traid_offset = %d (%08X)\n",
312                    disk->start, (int)raid_offset[ndisk], (int)raid_offset[ndisk]);
313             }
314         
315         /* derive the MBR name, which may be needed later */
316             strncpy(new_name,dev.name,MAX_TOKEN);
317             new_name[MAX_TOKEN] = '\0';
318             np = boot_mbr(dev.name, 0);
319             if (!np) np = stralloc(new_name);
320             raid_mbr[ndisk] = np;
321
322             if (ndisk==0) {     /* use the first disk geometry */
323                 md_disk->sectors = geo.sectors;
324                 md_disk->heads = geo.heads;
325                 md_disk->cylinders = geo.cylinders;
326                 md_disk->start = geo.start;
327             }
328             
329             ndisk++;  /* count the disk */
330         }  /* for (pass=...    */
331
332         dev_close(&dev);
333         raid_bios[ndisk] = 0;           /* mark the end */
334         raid_device[ndisk] = 0;
335
336         all_pri_eq &= raid_offset_set;
337         if (all_pri_eq && do_md_install == MD_SKEWED) {
338             do_md_install = MD_MIXED;
339         }
340         else pri_index = lowest;
341
342         autocount = 0;
343         /* check that all devices have an accessible block for writeback info */
344         for (pass=0; pass < ndisk; pass++) {
345             if (extra == X_MBR_ONLY || extra == X_MBR)
346                 raid_bios[pass] |= TO_BE_COVERED;
347
348             if (extra == X_AUTO /*&& raid_bios[pass] != 0x80*/) {
349                 if (do_md_install == MD_SKEWED)  {
350                     raid_bios[pass] |= TO_BE_COVERED;
351                     autocount++;
352                 }
353                 if (do_md_install == MD_MIXED) {
354                     if (is_primary(raid_device[pass])) raid_bios[pass] |= IS_COVERED;
355                     else  {
356                         raid_bios[pass] |= TO_BE_COVERED;
357                         autocount++;
358                     }
359                 }
360             }
361             if (extra != X_MBR)
362             if ((do_md_install == MD_PARALLEL && is_accessible(raid_device[pass]))
363                 || (do_md_install == MD_MIXED && pri_offset == raid_offset[pass]
364                         && is_primary(raid_device[pass]))
365                 )    
366                 raid_bios[pass] |= IS_COVERED;
367         }
368                 
369         nlist = 0;
370         if (extra==X_SPEC) {
371             char *next, *scan;
372             
373             scan = next = extrap;
374             while (next && *next) {
375                 scan = next;
376                 while (isspace(*scan)) scan++;  /* deblank the line */
377                 next = strchr(scan, ',');       /* find the separator */
378                 if (next) *next++ = 0;          /* NUL terminate  scan */
379                     
380                 if ((md_fd=open(scan,O_NOACCESS)) < 0)
381                     die("Unable to open %s", scan);
382                 if (fstat(md_fd,&st) < 0)
383                     die("Unable to stat %s",scan);
384                 if (!S_ISBLK(st.st_mode))
385                     die("%s (%04X) not a block device", scan, (int)st.st_rdev);
386                 if (verbose>=4) printf("RAID list: %s is device 0x%04X\n",
387                                 scan, (int)st.st_rdev);         
388                 close(md_fd);
389                 
390                 list_index[nlist] = ndisk;  /* raid_bios==0 here */
391                 for (pass=0; pass < ndisk; pass++) {
392                     if (master(st.st_rdev) == master(raid_device[pass])) {
393                         list_index[nlist] = pass;
394                         if (st.st_rdev == raid_device[pass])
395                             die("Cannot write to a partition within a RAID set:  %s", scan);
396                         else if (is_accessible(st.st_rdev))
397                             raid_bios[pass] |= IS_COVERED;
398                         break;
399                     }
400                 }
401                 if (list_index[nlist] == ndisk) {
402 #ifdef FLAG_RAID_NOWRITE
403                     raid_flags |= FLAG_RAID_NOWRITE;  /* disk is outside RAID set */
404 #endif
405                     if (!nowarn) printf("Warning: device outside of RAID set  %s  0x%04X\n", 
406                                                 scan, (int)st.st_rdev);
407                 }
408                 raid_list[nlist++] = stralloc(scan);
409             }
410             
411         }
412         
413                 
414     /* if the install is to MBRs, then change the boot= name */
415         if (extra == X_MBR_ONLY) {
416 #if 0
417             if (cfg_get_strg(cf_options,"boot")) cfg_unset(cf_options,"boot");
418             cfg_set(cf_options, "boot", (boot=raid_mbr[0]), NULL);
419 #endif
420         }
421         else {  /* if skewed install, disable mdX boot records as 
422                                                         source of writeback info */
423             if (do_md_install == MD_SKEWED) raid_flags |= FLAG_RAID_DEFEAT
424 #ifdef FLAG_RAID_NOWRITE
425                         | (extra == X_NONE ? FLAG_RAID_NOWRITE : 0)
426 #endif
427                         ;
428         }
429
430         mask = 1;
431         for (pass=0; pass < ndisk; pass++) {
432             mask &= !!(raid_bios[pass] & COVERED);
433         }
434 #ifdef FLAG_RAID_NOWRITE
435         if (!mask) {
436             raid_flags |= FLAG_RAID_NOWRITE;
437         }
438
439         if (raid_flags & FLAG_RAID_NOWRITE) {
440             warn("FLAG_RAID_NOWRITE has been set.");
441         }
442 #endif
443
444     /* if the disk= bios= did not specify the bios, then this is the default */
445         if (md_disk->bios < 0) {
446             md_disk->bios = md_bios;
447         }
448         md_bios = md_disk->bios;
449         if (md_disk->bios < 0x80 || md_disk->bios > DEV_MASK)
450            die("Unusual RAID bios device code: 0x%02X", md_disk->bios);
451 #if 0
452 /* Assigning all disks the same bios code is OBSOLETE in 22.5.6 */
453         disk = disktab;
454         for (pass=0; pass < ndisk; pass++) {
455             disk->bios = md_disk->bios;   /* all disks in the array are */
456             disk = disk->next;            /*  assigned the same bios code */
457         }
458 #endif
459         if (verbose) {
460             printf(
461                "Using BIOS device code 0x%02X for RAID boot blocks\n",
462                                             md_disk->bios);
463         }
464
465 #if 0
466         if ( mask &&  ( extra == X_NONE ||
467                         (extra == X_AUTO  &&  autocount == 0) ) )  {
468             if (bios_passes_dl > DL_BAD) bios_passes_dl = DL_GOOD;
469         }
470 #endif
471         
472         if (bios_passes_dl==DL_GOOD  &&  !(extra == X_MBR_ONLY || extra == X_MBR))
473             warn("Boot sector on  %s  will depend upon the BIOS device code\n"
474                 "  passed in the DL register being accurate.  Install Master Boot Records\n"
475                 "  with the 'lilo -M' command, and activate the RAID1 partitions with the\n"
476                 "  'lilo -A' command.",
477                 boot );
478         
479         return raid_offset[pri_index];
480     }   /* IF (test for a raid installation */
481 }  /* int raid_setup(void) */
482
483
484
485 void raid_final(void)
486 {
487     int pass, force = 0;
488     char *cp = NULL;
489     int mask = FLAG_SAVE;
490
491
492     if (bios_passes_dl < DL_GOOD) mask &= ~FLAG_MAP_ON_BOOT;
493     
494     if (test) /* do nothing */;
495     else if ((cp=cfg_get_strg(cf_options,"force-backup"))) force=1;
496     else cp=cfg_get_strg(cf_options,"backup");
497             
498     if (verbose>=2) {
499         printf("do_md_install: %s\n", do_md_install == MD_PARALLEL ? "MD_PARALLEL" :
500                 do_md_install == MD_MIXED ? "MD_MIXED" :
501                 do_md_install == MD_SKEWED ? "MD_SKEWED" : "unknown");
502         for (pass=0; pass<ndisk; pass++)
503             printf("  offset %08X  %s\n", raid_offset[pass], raid_mbr[pass]);
504     }
505                 
506     if (extra == X_MBR_ONLY) {    
507         pass = 0;
508         while (pass < ndisk) {
509 #ifndef LCF_UNIFY
510 # error "Bios Translation algorithms require '-DUNIFY' in Makefile"
511 #endif
512             if (pass==0 && test) {
513                 bsect_cancel();
514                 if (passw) printf("The password crc file has *NOT* been updated.\n");
515                 printf("The map file has *NOT* been altered.\n");
516             }
517
518             if (!test) {
519                 bsect_raid_update(raid_mbr[pass], raid_offset[pass],
520                         cp, force, pass ? pass : -1, mask );
521             }
522
523             printf("The Master boot "
524                 "record of  %s  has%s been updated.\n",
525                         raid_mbr[pass],
526                         test ? " *NOT*" : "" );
527             pass++;
528         }
529     } 
530     else {      /*  extra != X_MBR_ONLY   */
531         
532 #ifdef FLAG_RAID_DEFEAT
533         raid_flags &= ~FLAG_RAID_DEFEAT;    /* change won't affect /dev/mdX */
534 #endif
535         {
536           if (test) {
537             bsect_cancel();
538             if (passw) printf("The password crc file has *NOT* been updated.\n");
539             printf("The map file has *NOT* been updated.\n");
540           }
541           else {
542         /* write out the /dev/mdX boot records */           
543             bsect_raid_update(boot, 0L, cp, force, 0, FLAG_SAVE);
544           }
545             printf("The boot record of  %s  has%s been updated.\n", 
546                         boot,
547                         test ? " *NOT*" : "");
548         }
549
550         if (extra == X_NONE ||
551                 (extra == X_AUTO  &&  autocount == 0) ) return;
552
553         if (extra == X_SPEC)
554         for (pass = 0; pass < nlist; pass++) {
555             int index;
556
557             if (raid_bios[list_index[pass]] & 0xFF) {
558                 index = list_index[pass];       /* within RAID set */
559             }
560             else {  /* not in the RAID set */
561 #ifdef FLAG_RAID_DEFEAT
562                 raid_flags |= FLAG_RAID_DEFEAT;  /* make outsider invisible */
563 #endif
564                 index = lowest;
565             }
566
567             if (verbose>=3) printf("Specified partition:  %s  raid offset = %08X\n",
568                                         raid_list[pass], raid_offset[index]);
569
570             if (!test)
571                 bsect_raid_update(raid_list[pass], raid_offset[index], cp, force, 1, mask);
572             
573             printf("The boot record of  %s  has%s been updated.\n",
574                         raid_list[pass], (test ? " *NOT*" : ""));
575
576 #ifdef FLAG_RAID_DEFEAT
577             raid_flags &= ~FLAG_RAID_DEFEAT; /* restore DEFEAT flag to 0 */
578 #endif
579         }
580         else {          /* extra = X_AUTO or X_MBR*/
581             for (pass = 0; pass < ndisk; pass++) {
582                 if (!(raid_bios[pass] & IS_COVERED)) {
583                     if ((raid_bios[pass] & 0xFF) != 0x80  || extra == X_MBR) {
584                         if (!test)
585                             bsect_raid_update(raid_mbr[pass], raid_offset[pass],
586                                                         cp, force, 1, mask);
587                         printf("The Master boot record of  %s  has%s been updated.\n",
588                                         raid_mbr[pass], (test ? " *NOT*" : ""));
589                     } else {
590                         warn("%splicit AUTO does not allow updating the Master Boot Record\n"
591                                 "  of '%s' on BIOS device code 0x80, the System Master Boot Record.\n"
592                                 "  You must explicitly specify updating of this boot sector with\n"
593                                 "  '-x %s' or 'raid-extra-boot = %s' in the\n"
594                                 "  configuration file.",
595                                 cfg_get_strg(cf_options, RAID_EXTRA_BOOT) ? "Ex" : "Im",
596                                 raid_mbr[pass],
597                                 raid_mbr[pass],
598                                 raid_mbr[pass] 
599                                 );
600                     }
601                 }
602             }
603         }
604     }
605
606
607 #ifdef FLAG_RAID_NOWRITE
608     if (raid_flags & FLAG_RAID_NOWRITE) {
609         warn("FLAG_RAID_NOWRITE has been set.%s", verbose>=1 ?
610                 "\n  The boot loader will be unable to update the stored command line;\n"
611                 "  'lock' and 'fallback' are not operable; the 'lilo -R' boot command\n"
612                 "  line will be locked." : "" );
613     }
614 #endif
615
616 }
617
618 /* form the mask of the raid bios codes and the list of offsets */
619 /* this information goes into the MENUTABLE passed to the loader */
620 int raid_mask(int *offsets)
621 {
622     int mask = 0;
623     int i, j;
624     int offset[MAX_BIOS_DEVICES];
625     
626     if (ndisk > MAX_RAID_DEVICES)
627         die("More than %d active RAID1 disks", MAX_RAID_DEVICES);
628         
629     memset(offset, 0, sizeof(offset));
630     for (i=0; i<ndisk; i++) {
631         offset[j = raid_bios[i] & (DEV_MASK & 0x7F) ] = raid_offset[i];
632         mask |= 1<<j;
633     }
634     for (i=0; i<nelem(offset); i++) {
635         if ( (mask>>i) & 1 ) *offsets++ = offset[i];
636         if (do_md_install && verbose>=3) printf("RAID offset entry %d  0x%08X\n", offset[i], offset[i]);
637     }
638     
639     if (verbose>=2) printf("RAID device mask 0x%04X\n", mask);
640     
641     return mask;
642 }
643
644