1 /*
2
3 File: md.c
4
5 Copyright (C) 1998-2008 Christophe GRENIER <grenier@cgsecurity.org>
6
7 This software is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write the Free Software Foundation, Inc., 51
19 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 */
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33 #include "types.h"
34 #include "common.h"
35 #include "md.h"
36 #include "fnctdsk.h"
37 #include "log.h"
38 static int test_MD(disk_t *disk_car, const struct mdp_superblock_s *sb, const partition_t *partition, const int dump_ind);
39 static int test_MD_be(disk_t *disk_car, const struct mdp_superblock_s *sb, const partition_t *partition, const int dump_ind);
40 static void set_MD_info(const struct mdp_superblock_s *sb, partition_t *partition, const int verbose);
41 static void set_MD_info_be(const struct mdp_superblock_s *sb, partition_t *partition, const int verbose);
42
check_MD(disk_t * disk_car,partition_t * partition,const int verbose)43 int check_MD(disk_t *disk_car, partition_t *partition, const int verbose)
44 {
45 unsigned char *buffer=(unsigned char*)MALLOC(MD_SB_BYTES);
46 /* MD version 1.1 */
47 if(disk_car->pread(disk_car, buffer, MD_SB_BYTES, partition->part_offset) == MD_SB_BYTES)
48 {
49 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)buffer;
50 if(le32(sb1->md_magic)==(unsigned int)MD_SB_MAGIC &&
51 le32(sb1->major_version)==1 &&
52 le64(sb1->super_offset)==0 &&
53 test_MD(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
54 {
55 log_info("check_MD 1.1\n");
56 set_MD_info((struct mdp_superblock_s*)buffer, partition, verbose);
57 free(buffer);
58 return 0;
59 }
60 if(be32(sb1->md_magic)==(unsigned int)MD_SB_MAGIC &&
61 be32(sb1->major_version)==1 &&
62 be64(sb1->super_offset)==0 &&
63 test_MD_be(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
64 {
65 log_info("check_MD 1.1 (BigEndian)\n");
66 set_MD_info_be((struct mdp_superblock_s*)buffer, partition, verbose);
67 free(buffer);
68 return 0;
69 }
70 }
71 /* MD version 1.2 */
72 if(disk_car->pread(disk_car, buffer, MD_SB_BYTES, partition->part_offset + 4096) == MD_SB_BYTES)
73 {
74 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)buffer;
75 if(le32(sb1->md_magic)==(unsigned int)MD_SB_MAGIC &&
76 le32(sb1->major_version)==1 &&
77 le64(sb1->super_offset)==8 &&
78 test_MD(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
79 {
80 log_info("check_MD 1.2\n");
81 set_MD_info((struct mdp_superblock_s*)buffer, partition, verbose);
82 free(buffer);
83 return 0;
84 }
85 if(be32(sb1->md_magic)==(unsigned int)MD_SB_MAGIC &&
86 be32(sb1->major_version)==1 &&
87 be64(sb1->super_offset)==8 &&
88 test_MD_be(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
89 {
90 log_info("check_MD 1.2 (BigEndian)\n");
91 set_MD_info_be((struct mdp_superblock_s*)buffer, partition, verbose);
92 free(buffer);
93 return 0;
94 }
95 }
96 /* MD version 0.90 */
97 {
98 const struct mdp_superblock_s *sb=(const struct mdp_superblock_s *)buffer;
99 const uint64_t offset=MD_NEW_SIZE_SECTORS(partition->part_size/512)*512;
100 if(verbose>1)
101 {
102 log_verbose("Raid md 0.90 offset %llu\n", (long long unsigned)offset/512);
103 }
104 if(disk_car->pread(disk_car, buffer, MD_SB_BYTES, partition->part_offset + offset) == MD_SB_BYTES)
105 {
106 if(le32(sb->md_magic)==(unsigned int)MD_SB_MAGIC &&
107 le32(sb->major_version)==0 &&
108 test_MD(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
109 {
110 log_info("check_MD 0.90\n");
111 set_MD_info((struct mdp_superblock_s*)buffer, partition, verbose);
112 free(buffer);
113 return 0;
114 }
115 if(be32(sb->md_magic)==(unsigned int)MD_SB_MAGIC &&
116 be32(sb->major_version)==0 &&
117 test_MD_be(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
118 {
119 log_info("check_MD 0.90 (BigEndian)\n");
120 set_MD_info_be((struct mdp_superblock_s*)buffer, partition, verbose);
121 free(buffer);
122 return 0;
123 }
124 }
125 }
126 /* MD version 1.0 */
127 if(partition->part_size > 8*2*512)
128 {
129 const uint64_t offset=(uint64_t)(((partition->part_size/512)-8*2) & ~(4*2-1))*512;
130 if(verbose>1)
131 {
132 log_verbose("Raid md 1.0 offset %llu\n", (long long unsigned)offset/512);
133 }
134 if(disk_car->pread(disk_car, buffer, MD_SB_BYTES, partition->part_offset + offset) == MD_SB_BYTES)
135 {
136 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)buffer;
137 if(le32(sb1->md_magic)==(unsigned int)MD_SB_MAGIC &&
138 le32(sb1->major_version)==1 &&
139 le64(sb1->super_offset)==(offset/512) &&
140 test_MD(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
141 {
142 log_info("check_MD 1.0\n");
143 set_MD_info((struct mdp_superblock_s*)buffer, partition, verbose);
144 free(buffer);
145 return 0;
146 }
147 if(be32(sb1->md_magic)==(unsigned int)MD_SB_MAGIC &&
148 be32(sb1->major_version)==1 &&
149 be64(sb1->super_offset)==(offset/512) &&
150 test_MD_be(disk_car, (struct mdp_superblock_s*)buffer, partition, 0)==0)
151 {
152 log_info("check_MD 1.0 (BigEndian)\n");
153 set_MD_info_be((struct mdp_superblock_s*)buffer, partition, verbose);
154 free(buffer);
155 return 0;
156 }
157 }
158 }
159 free(buffer);
160 return 1;
161 }
162
recover_MD_from_partition(disk_t * disk_car,partition_t * partition,const int verbose)163 int recover_MD_from_partition(disk_t *disk_car, partition_t *partition, const int verbose)
164 {
165 unsigned char *buffer=(unsigned char*)MALLOC(MD_SB_BYTES);
166 /* MD version 0.90 */
167 {
168 uint64_t offset=MD_NEW_SIZE_SECTORS(partition->part_size/512)*512;
169 if(disk_car->pread(disk_car, buffer, MD_SB_BYTES, partition->part_offset + offset) == MD_SB_BYTES)
170 {
171 if(recover_MD(disk_car,(struct mdp_superblock_s*)buffer,partition,verbose,0)==0)
172 {
173 free(buffer);
174 return 0;
175 }
176 }
177 }
178 /* MD version 1.0 */
179 if(partition->part_size > 8*2*512)
180 {
181 uint64_t offset=(((partition->part_size/512)-8*2) & ~(4*2-1))*512;
182 if(disk_car->pread(disk_car, buffer, MD_SB_BYTES, partition->part_offset + offset) == MD_SB_BYTES)
183 {
184 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)buffer;
185 if(le32(sb1->major_version)==1 &&
186 recover_MD(disk_car,(struct mdp_superblock_s*)buffer,partition,verbose,0)==0)
187 {
188 partition->part_offset-=le64(sb1->super_offset)*512-offset;
189 free(buffer);
190 return 0;
191 }
192 }
193 }
194 /* md 1.1 & 1.2 don't need special operation to be recovered */
195 free(buffer);
196 return 1;
197 }
198
recover_MD(disk_t * disk_car,const struct mdp_superblock_s * sb,partition_t * partition,const int verbose,const int dump_ind)199 int recover_MD(disk_t *disk_car, const struct mdp_superblock_s *sb, partition_t *partition, const int verbose, const int dump_ind)
200 {
201 if(test_MD(disk_car, sb, partition, dump_ind)==0)
202 {
203 set_MD_info(sb, partition, verbose);
204 partition->part_type_i386=P_RAID;
205 partition->part_type_sun=PSUN_RAID;
206 partition->part_type_gpt=GPT_ENT_TYPE_LINUX_RAID;
207 if(le32(sb->major_version)==0)
208 {
209 partition->part_size=(uint64_t)(le32(sb->size)<<1)*512+MD_RESERVED_BYTES; /* 512-byte sectors */
210 memcpy(&partition->part_uuid, &sb->set_uuid0, 4);
211 memcpy((char*)(&partition->part_uuid)+4, &sb->set_uuid1, 3*4);
212 }
213 else
214 {
215 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)sb;
216 partition->part_size=(uint64_t)le64(sb1->size) * 512 + 4096; /* 512-byte sectors */
217 memcpy(&partition->part_uuid, &sb1->set_uuid, 16);
218 }
219 return 0;
220 }
221 if(test_MD_be(disk_car, sb, partition, dump_ind)==0)
222 {
223 set_MD_info_be(sb, partition, verbose);
224 partition->part_type_i386=P_RAID;
225 partition->part_type_sun=PSUN_RAID;
226 partition->part_type_gpt=GPT_ENT_TYPE_LINUX_RAID;
227 if(be32(sb->major_version)==0)
228 {
229 partition->part_size=(uint64_t)(be32(sb->size)<<1)*512+MD_RESERVED_BYTES; /* 512-byte sectors */
230 memcpy(&partition->part_uuid, &sb->set_uuid0, 4);
231 memcpy((char*)(&partition->part_uuid)+4, &sb->set_uuid1, 3*4);
232 }
233 else
234 {
235 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)sb;
236 partition->part_size=(uint64_t)be64(sb1->size) * 512 + 4096; /* 512-byte sectors */
237 memcpy(&partition->part_uuid, &sb1->set_uuid, 16);
238 }
239 return 0;
240 }
241 return 1;
242 }
243
set_MD_info(const struct mdp_superblock_s * sb,partition_t * partition,const int verbose)244 static void set_MD_info(const struct mdp_superblock_s *sb, partition_t *partition, const int verbose)
245 {
246 if(le32(sb->major_version)==0)
247 {
248 unsigned int i;
249 partition->upart_type=UP_MD;
250 sprintf(partition->fsname,"md%u",(unsigned int)le32(sb->md_minor));
251 sprintf(partition->info,"md %u.%u.%u L.Endian Raid %u: devices",
252 (unsigned int)le32(sb->major_version),
253 (unsigned int)le32(sb->minor_version),
254 (unsigned int)le32(sb->patch_version),
255 (unsigned int)le32(sb->level));
256 for(i=0;i<MD_SB_DISKS;i++)
257 {
258 if(le32(sb->disks[i].major)!=0 && le32(sb->disks[i].minor)!=0)
259 {
260 if(strlen(partition->info)<sizeof(partition->info)-26)
261 {
262 sprintf(&partition->info[strlen(partition->info)]," %u(%u,%u)",
263 (unsigned int)le32(sb->disks[i].number),
264 (unsigned int)le32(sb->disks[i].major),
265 (unsigned int)le32(sb->disks[i].minor));
266 if(le32(sb->disks[i].major)==le32(sb->this_disk.major) &&
267 le32(sb->disks[i].minor)==le32(sb->this_disk.minor))
268 sprintf(&partition->info[strlen(partition->info)],"*");
269 }
270 }
271 }
272 }
273 else
274 {
275 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)sb;
276 partition->upart_type=UP_MD1;
277 set_part_name(partition,sb1->set_name,32);
278 sprintf(partition->info,"md %u.x L.Endian Raid %u - Array Slot : %lu",
279 (unsigned int)le32(sb1->major_version),
280 (unsigned int)le32(sb1->level),
281 (long unsigned)le32(sb1->dev_number));
282 if(le32(sb1->max_dev) <= 384)
283 {
284 unsigned int i,d;
285 for (i= le32(sb1->max_dev); i> 0 ; i--)
286 if (le16(sb1->dev_roles[i-1]) != 0xffff)
287 break;
288 strcat(partition->info, " (");
289 for (d=0; d < i && strlen(partition->info) < sizeof(partition->info) - 9; d++)
290 {
291 const int role = le16(sb1->dev_roles[d]);
292 if (d)
293 strcat(partition->info, ", ");
294 if (role == 0xffff)
295 strcat(partition->info, "empty");
296 else if(role == 0xfffe)
297 strcat(partition->info, "failed");
298 else
299 sprintf(&partition->info[strlen(partition->info)], "%d", role);
300 }
301 strcat(partition->info, ")");
302 }
303 }
304 if(verbose>0)
305 log_info("%s %s\n", partition->fsname, partition->info);
306 }
307
set_MD_info_be(const struct mdp_superblock_s * sb,partition_t * partition,const int verbose)308 static void set_MD_info_be(const struct mdp_superblock_s *sb, partition_t *partition, const int verbose)
309 {
310 if(be32(sb->major_version)==0)
311 {
312 unsigned int i;
313 partition->upart_type=UP_MD;
314 sprintf(partition->fsname,"md%u",(unsigned int)be32(sb->md_minor));
315 sprintf(partition->info,"md %u.%u.%u B.Endian Raid %u: devices",
316 (unsigned int)be32(sb->major_version),
317 (unsigned int)be32(sb->minor_version),
318 (unsigned int)be32(sb->patch_version),
319 (unsigned int)be32(sb->level));
320 for(i=0;i<MD_SB_DISKS;i++)
321 {
322 if(be32(sb->disks[i].major)!=0 && be32(sb->disks[i].minor)!=0)
323 {
324 if(strlen(partition->info)<sizeof(partition->info)-26)
325 {
326 sprintf(&partition->info[strlen(partition->info)]," %u(%u,%u)",
327 (unsigned int)be32(sb->disks[i].number),
328 (unsigned int)be32(sb->disks[i].major),
329 (unsigned int)be32(sb->disks[i].minor));
330 if(be32(sb->disks[i].major)==be32(sb->this_disk.major) &&
331 be32(sb->disks[i].minor)==be32(sb->this_disk.minor))
332 sprintf(&partition->info[strlen(partition->info)],"*");
333 }
334 }
335 }
336 }
337 else
338 {
339 const struct mdp_superblock_1 *sb1=(const struct mdp_superblock_1 *)sb;
340 partition->upart_type=UP_MD1;
341 set_part_name(partition,sb1->set_name,32);
342 sprintf(partition->info,"md %u.x B.Endian Raid %u - Array Slot : %lu",
343 (unsigned int)be32(sb1->major_version),
344 (unsigned int)be32(sb1->level),
345 (long unsigned)be32(sb1->dev_number));
346 if(be32(sb1->max_dev) <= 384)
347 {
348 unsigned int i,d;
349 for (i= be32(sb1->max_dev); i> 0 ; i--)
350 if (be16(sb1->dev_roles[i-1]) != 0xffff)
351 break;
352 strcat(partition->info, " (");
353 for (d=0; d < i && strlen(partition->info) < sizeof(partition->info) - 9; d++)
354 {
355 const int role = be16(sb1->dev_roles[d]);
356 if (d)
357 strcat(partition->info, ", ");
358 if (role == 0xffff)
359 strcat(partition->info, "empty");
360 else if(role == 0xfffe)
361 strcat(partition->info, "failed");
362 else
363 sprintf(&partition->info[strlen(partition->info)], "%d", role);
364 }
365 strcat(partition->info, ")");
366 }
367 }
368 if(verbose>0)
369 log_info("%s %s\n", partition->fsname, partition->info);
370 }
371
test_MD(disk_t * disk_car,const struct mdp_superblock_s * sb,const partition_t * partition,const int dump_ind)372 static int test_MD(disk_t *disk_car, const struct mdp_superblock_s *sb, const partition_t *partition, const int dump_ind)
373 {
374 if(le32(sb->md_magic)!=(unsigned int)MD_SB_MAGIC)
375 return 1;
376 log_info("\nRaid magic value at %u/%u/%u\n",
377 offset2cylinder(disk_car,partition->part_offset),
378 offset2head(disk_car,partition->part_offset),
379 offset2sector(disk_car,partition->part_offset));
380 log_info("Raid apparent size: %llu sectors\n", (long long unsigned)(sb->size<<1));
381 if(le32(sb->major_version)==0)
382 {
383 /* chunk_size may be 0 */
384 log_info("Raid chunk size: %llu bytes\n", (long long unsigned)le32(sb->chunk_size));
385 }
386 if(le32(sb->major_version)>1)
387 return 1;
388 if(dump_ind!=0)
389 {
390 /* There is a little offset ... */
391 dump_log(sb,DEFAULT_SECTOR_SIZE);
392 }
393 return 0;
394 }
395
test_MD_be(disk_t * disk_car,const struct mdp_superblock_s * sb,const partition_t * partition,const int dump_ind)396 static int test_MD_be(disk_t *disk_car, const struct mdp_superblock_s *sb, const partition_t *partition, const int dump_ind)
397 {
398 if(be32(sb->md_magic)!=(unsigned int)MD_SB_MAGIC)
399 return 1;
400 log_info("\nRaid magic value at %u/%u/%u\n",
401 offset2cylinder(disk_car,partition->part_offset),
402 offset2head(disk_car,partition->part_offset),
403 offset2sector(disk_car,partition->part_offset));
404 log_info("Raid apparent size: %llu sectors\n", (long long unsigned)(sb->size<<1));
405 if(be32(sb->major_version)==0)
406 {
407 /* chunk_size may be 0 */
408 log_info("Raid chunk size: %llu bytes\n",(long long unsigned)be32(sb->chunk_size));
409 }
410 if(be32(sb->major_version)>1)
411 return 1;
412 if(dump_ind!=0)
413 {
414 /* There is a little offset ... */
415 dump_log(sb,DEFAULT_SECTOR_SIZE);
416 }
417 return 0;
418 }
419