1 /* pc.c - Read PC style partition tables. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002,2004,2005,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/>.
20 #include <grub/partition.h>
21 #include <grub/msdos_partition.h>
22 #include <grub/disk.h>
24 #include <grub/misc.h>
27 static struct grub_partition_map grub_msdos_partition_map
;
30 /* Parse the partition representation in STR and return a partition. */
31 static grub_partition_t
32 grub_partition_parse (const char *str
)
35 struct grub_msdos_partition
*pcdata
;
37 char *s
= (char *) str
;
39 p
= (grub_partition_t
) grub_malloc (sizeof (*p
));
43 pcdata
= (struct grub_msdos_partition
*) grub_malloc (sizeof (*pcdata
));
48 p
->partmap
= &grub_msdos_partition_map
;
50 /* Initialize some of the fields with invalid values. */
51 pcdata
->bsd_part
= pcdata
->dos_type
= pcdata
->bsd_type
= p
->index
= -1;
53 /* Get the DOS partition number. The number is counted from one for
54 the user interface, and from zero internally. */
55 pcdata
->dos_part
= grub_strtoul (s
, &s
, 0) - 1;
59 /* Not found. Maybe only a BSD label is specified. */
60 pcdata
->dos_part
= -1;
61 grub_errno
= GRUB_ERR_NONE
;
68 if (*s
>= 'a' && *s
<= 'h')
70 pcdata
->bsd_part
= *s
- 'a';
78 if (pcdata
->dos_part
== -1 && pcdata
->bsd_part
== -1)
86 grub_error (GRUB_ERR_BAD_FILENAME
, "invalid partition");
91 pc_partition_map_iterate (grub_disk_t disk
,
92 int (*hook
) (grub_disk_t disk
,
93 const grub_partition_t partition
))
95 struct grub_partition p
;
96 struct grub_msdos_partition pcdata
;
97 struct grub_msdos_partition_mbr mbr
;
98 struct grub_msdos_partition_disk_label label
;
101 grub_disk_addr_t lastaddr
;
103 /* Enforce raw disk access. */
108 pcdata
.ext_offset
= 0;
109 pcdata
.dos_part
= -1;
111 p
.partmap
= &grub_msdos_partition_map
;
113 /* Any value different than `p.offset' will satisfy the check during
115 lastaddr
= !p
.offset
;
120 struct grub_msdos_partition_entry
*e
;
123 if (grub_disk_read (&raw
, p
.offset
, 0, sizeof (mbr
), &mbr
))
126 /* This is our loop-detection algorithm. It works the following way:
127 It saves last position which was a power of two. Then it compares the
128 saved value with a current one. This way it's guaranteed that the loop
129 will be broken by at most third walk.
131 if (labeln
&& lastaddr
== p
.offset
)
132 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "loop detected");
135 if ((labeln
& (labeln
- 1)) == 0)
138 /* Check if it is valid. */
139 if (mbr
.signature
!= grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE
))
140 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "no signature");
142 for (i
= 0; i
< 4; i
++)
143 if (mbr
.entries
[i
].flag
& 0x7f)
144 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "bad boot flag");
146 /* Analyze DOS partitions. */
147 for (p
.index
= 0; p
.index
< 4; p
.index
++)
149 e
= mbr
.entries
+ p
.index
;
151 p
.start
= p
.offset
+ grub_le_to_cpu32 (e
->start
);
152 p
.len
= grub_le_to_cpu32 (e
->length
);
153 pcdata
.bsd_part
= -1;
154 pcdata
.dos_type
= e
->type
;
155 pcdata
.bsd_type
= -1;
157 grub_dprintf ("partition",
158 "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n",
159 p
.index
, e
->flag
, pcdata
.dos_type
,
160 (unsigned long long) p
.start
,
161 (unsigned long long) p
.len
);
163 /* If this is a GPT partition, this MBR is just a dummy. */
164 if (e
->type
== GRUB_PC_PARTITION_TYPE_GPT_DISK
&& p
.index
== 0)
165 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "dummy mbr");
167 /* If this partition is a normal one, call the hook. */
168 if (! grub_msdos_partition_is_empty (e
->type
)
169 && ! grub_msdos_partition_is_extended (e
->type
))
176 /* Check if this is a BSD partition. */
177 if (grub_msdos_partition_is_bsd (e
->type
))
179 /* Check if the BSD label is within the DOS partition. */
180 if (p
.len
<= GRUB_PC_PARTITION_BSD_LABEL_SECTOR
)
182 grub_dprintf ("partition", "no space for disk label\n");
185 /* Read the BSD label. */
186 if (grub_disk_read (&raw
,
188 + GRUB_PC_PARTITION_BSD_LABEL_SECTOR
),
194 /* Check if it is valid. */
196 != grub_cpu_to_le32 (GRUB_PC_PARTITION_BSD_LABEL_MAGIC
))
198 grub_dprintf ("partition",
199 "invalid disk label magic 0x%x on partition %d\n",
200 label
.magic
, p
.index
);
203 for (pcdata
.bsd_part
= 0;
204 pcdata
.bsd_part
< grub_cpu_to_le16 (label
.num_partitions
);
207 struct grub_msdos_partition_bsd_entry
*be
208 = label
.entries
+ pcdata
.bsd_part
;
210 p
.start
= grub_le_to_cpu32 (be
->offset
);
211 p
.len
= grub_le_to_cpu32 (be
->size
);
212 pcdata
.bsd_type
= be
->fs_type
;
214 if (be
->fs_type
!= GRUB_PC_PARTITION_BSD_TYPE_UNUSED
)
220 else if (pcdata
.dos_part
< 4)
221 /* If this partition is a logical one, shouldn't increase the
226 /* Find an extended partition. */
227 for (i
= 0; i
< 4; i
++)
231 if (grub_msdos_partition_is_extended (e
->type
))
233 p
.offset
= pcdata
.ext_offset
+ grub_le_to_cpu32 (e
->start
);
234 if (! pcdata
.ext_offset
)
235 pcdata
.ext_offset
= p
.offset
;
241 /* If no extended partition, the end. */
251 static grub_partition_t
252 pc_partition_map_probe (grub_disk_t disk
, const char *str
)
255 struct grub_msdos_partition
*pcdata
;
257 auto int find_func (grub_disk_t d
, const grub_partition_t partition
);
259 int find_func (grub_disk_t d
__attribute__ ((unused
)),
260 const grub_partition_t partition
)
262 struct grub_msdos_partition
*partdata
= partition
->data
;
264 if ((pcdata
->dos_part
== partdata
->dos_part
|| pcdata
->dos_part
== -1)
265 && pcdata
->bsd_part
== partdata
->bsd_part
)
267 grub_memcpy (p
, partition
, sizeof (*p
));
269 grub_memcpy (pcdata
, partdata
, sizeof (*pcdata
));
276 p
= grub_partition_parse (str
);
281 pc_partition_map_iterate (disk
, find_func
);
287 grub_error (GRUB_ERR_BAD_DEVICE
, "no such partition");
301 pc_partition_map_get_name (const grub_partition_t p
)
304 struct grub_msdos_partition
*pcdata
= p
->data
;
306 name
= grub_malloc (13);
310 if (pcdata
->bsd_part
< 0)
311 grub_sprintf (name
, "%d", pcdata
->dos_part
+ 1);
312 else if (pcdata
->dos_part
< 0)
313 grub_sprintf (name
, "%c", pcdata
->bsd_part
+ 'a');
315 grub_sprintf (name
, "%d,%c", pcdata
->dos_part
+ 1, pcdata
->bsd_part
+ 'a');
321 /* Partition map type. */
322 static struct grub_partition_map grub_msdos_partition_map
=
324 .name
= "part_msdos",
325 .iterate
= pc_partition_map_iterate
,
326 .probe
= pc_partition_map_probe
,
327 .get_name
= pc_partition_map_get_name
330 GRUB_MOD_INIT(pc_partition_map
)
332 grub_partition_map_register (&grub_msdos_partition_map
);
335 GRUB_MOD_FINI(pc_partition_map
)
337 grub_partition_map_unregister (&grub_msdos_partition_map
);