1 /* raid6_recover.c - module to recover from faulty RAID6 arrays. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/disk.h>
24 #include <grub/misc.h>
25 #include <grub/diskfilter.h>
26 #include <grub/crypto.h>
28 GRUB_MOD_LICENSE ("GPLv3+");
31 static grub_uint8_t powx
[255 * 2];
32 /* Such an s that x**s = y */
33 static unsigned powx_inv
[256];
34 static const grub_uint8_t poly
= 0x1d;
37 grub_raid_block_mulx (unsigned mul
, char *buf
, grub_size_t size
)
42 p
= (grub_uint8_t
*) buf
;
43 for (i
= 0; i
< size
; i
++, p
++)
45 *p
= powx
[mul
+ powx_inv
[*p
]];
49 grub_raid6_init_table (void)
54 for (i
= 0; i
< 255; i
++)
60 cur
= (cur
<< 1) ^ poly
;
67 grub_raid6_recover (struct grub_diskfilter_segment
*array
, int disknr
, int p
,
68 char *buf
, grub_disk_addr_t sector
, grub_size_t size
)
71 int bad1
= -1, bad2
= -1;
72 char *pbuf
= 0, *qbuf
= 0;
74 size
<<= GRUB_DISK_SECTOR_BITS
;
75 pbuf
= grub_zalloc (size
);
79 qbuf
= grub_zalloc (size
);
84 if (q
== (int) array
->node_count
)
88 if (pos
== (int) array
->node_count
)
91 for (i
= 0; i
< (int) array
->node_count
- 2; i
++)
94 if (array
->layout
& GRUB_RAID_LAYOUT_MUL_FROM_POS
)
102 if (! grub_diskfilter_read_node (&array
->nodes
[pos
], sector
,
103 size
>> GRUB_DISK_SECTOR_BITS
, buf
))
105 grub_crypto_xor (pbuf
, pbuf
, buf
, size
);
106 grub_raid_block_mulx (c
, buf
, size
);
107 grub_crypto_xor (qbuf
, qbuf
, buf
, size
);
111 /* Too many bad devices */
116 grub_errno
= GRUB_ERR_NONE
;
121 if (pos
== (int) array
->node_count
)
125 /* Invalid disknr or p */
132 if ((! grub_diskfilter_read_node (&array
->nodes
[p
], sector
,
133 size
>> GRUB_DISK_SECTOR_BITS
, buf
)))
135 grub_crypto_xor (buf
, buf
, pbuf
, size
);
139 grub_errno
= GRUB_ERR_NONE
;
140 if (grub_diskfilter_read_node (&array
->nodes
[q
], sector
,
141 size
>> GRUB_DISK_SECTOR_BITS
, buf
))
144 grub_crypto_xor (buf
, buf
, qbuf
, size
);
145 grub_raid_block_mulx (255 - bad1
, buf
,
150 /* Two bad devices */
153 if (grub_diskfilter_read_node (&array
->nodes
[p
], sector
,
154 size
>> GRUB_DISK_SECTOR_BITS
, buf
))
157 grub_crypto_xor (pbuf
, pbuf
, buf
, size
);
159 if (grub_diskfilter_read_node (&array
->nodes
[q
], sector
,
160 size
>> GRUB_DISK_SECTOR_BITS
, buf
))
163 grub_crypto_xor (qbuf
, qbuf
, buf
, size
);
166 + (255 ^ powx_inv
[(powx
[bad2
+ (bad1
^ 255)] ^ 1)])) % 255;
167 grub_raid_block_mulx (c
, qbuf
, size
);
169 c
= ((unsigned) bad2
+ c
) % 255;
170 grub_raid_block_mulx (c
, pbuf
, size
);
172 grub_crypto_xor (pbuf
, pbuf
, qbuf
, size
);
173 grub_memcpy (buf
, pbuf
, size
);
183 GRUB_MOD_INIT(raid6rec
)
185 grub_raid6_init_table ();
186 grub_raid6_recover_func
= grub_raid6_recover
;
189 GRUB_MOD_FINI(raid6rec
)
191 grub_raid6_recover_func
= 0;