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