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/pc_partition.h>
22 #include <grub/disk.h>
24 #include <grub/misc.h>
27 static struct grub_partition_map grub_pc_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_pc_partition
*pcdata
;
37 char *s
= (char *) str
;
39 p
= (grub_partition_t
) grub_malloc (sizeof (*p
));
43 pcdata
= (struct grub_pc_partition
*) grub_malloc (sizeof (*pcdata
));
48 p
->partmap
= &grub_pc_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_pc_partition pcdata
;
97 struct grub_pc_partition_mbr mbr
;
98 struct grub_pc_partition_disk_label label
;
101 /* Enforce raw disk access. */
106 pcdata
.ext_offset
= 0;
107 pcdata
.dos_part
= -1;
109 p
.partmap
= &grub_pc_partition_map
;
114 struct grub_pc_partition_entry
*e
;
117 if (grub_disk_read (&raw
, p
.offset
, 0, sizeof (mbr
), &mbr
))
120 /* Check if it is valid. */
121 if (mbr
.signature
!= grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE
))
122 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "no signature");
124 /* Analyze DOS partitions. */
125 for (p
.index
= 0; p
.index
< 4; p
.index
++)
127 e
= mbr
.entries
+ p
.index
;
129 p
.start
= p
.offset
+ grub_le_to_cpu32 (e
->start
);
130 p
.len
= grub_le_to_cpu32 (e
->length
);
131 pcdata
.bsd_part
= -1;
132 pcdata
.dos_type
= e
->type
;
133 pcdata
.bsd_type
= -1;
135 grub_dprintf ("partition",
136 "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n",
137 p
.index
, e
->flag
, pcdata
.dos_type
,
138 (unsigned long long) p
.start
,
139 (unsigned long long) p
.len
);
141 /* If this is a GPT partition, this MBR is just a dummy. */
142 if (e
->type
== GRUB_PC_PARTITION_TYPE_GPT_DISK
&& p
.index
== 0)
143 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "dummy mbr");
145 /* If this partition is a normal one, call the hook. */
146 if (! grub_pc_partition_is_empty (e
->type
)
147 && ! grub_pc_partition_is_extended (e
->type
))
154 /* Check if this is a BSD partition. */
155 if (grub_pc_partition_is_bsd (e
->type
))
157 /* Check if the BSD label is within the DOS partition. */
158 if (p
.len
<= GRUB_PC_PARTITION_BSD_LABEL_SECTOR
)
160 grub_dprintf ("partition", "no space for disk label\n");
163 /* Read the BSD label. */
164 if (grub_disk_read (&raw
,
166 + GRUB_PC_PARTITION_BSD_LABEL_SECTOR
),
172 /* Check if it is valid. */
174 != grub_cpu_to_le32 (GRUB_PC_PARTITION_BSD_LABEL_MAGIC
))
176 grub_dprintf ("partition",
177 "invalid disk label magic 0x%x on partition %d\n",
178 label
.magic
, p
.index
);
181 for (pcdata
.bsd_part
= 0;
182 pcdata
.bsd_part
< grub_cpu_to_le16 (label
.num_partitions
);
185 struct grub_pc_partition_bsd_entry
*be
186 = label
.entries
+ pcdata
.bsd_part
;
188 p
.start
= grub_le_to_cpu32 (be
->offset
);
189 p
.len
= grub_le_to_cpu32 (be
->size
);
190 pcdata
.bsd_type
= be
->fs_type
;
192 if (be
->fs_type
!= GRUB_PC_PARTITION_BSD_TYPE_UNUSED
)
198 else if (pcdata
.dos_part
< 4)
199 /* If this partition is a logical one, shouldn't increase the
204 /* Find an extended partition. */
205 for (i
= 0; i
< 4; i
++)
209 if (grub_pc_partition_is_extended (e
->type
))
211 p
.offset
= pcdata
.ext_offset
+ grub_le_to_cpu32 (e
->start
);
212 if (! pcdata
.ext_offset
)
213 pcdata
.ext_offset
= p
.offset
;
219 /* If no extended partition, the end. */
229 static grub_partition_t
230 pc_partition_map_probe (grub_disk_t disk
, const char *str
)
233 struct grub_pc_partition
*pcdata
;
235 auto int find_func (grub_disk_t d
, const grub_partition_t partition
);
237 int find_func (grub_disk_t d
__attribute__ ((unused
)),
238 const grub_partition_t partition
)
240 struct grub_pc_partition
*partdata
= partition
->data
;
242 if ((pcdata
->dos_part
== partdata
->dos_part
|| pcdata
->dos_part
== -1)
243 && pcdata
->bsd_part
== partdata
->bsd_part
)
245 grub_memcpy (p
, partition
, sizeof (*p
));
247 grub_memcpy (pcdata
, partdata
, sizeof (*pcdata
));
254 p
= grub_partition_parse (str
);
259 pc_partition_map_iterate (disk
, find_func
);
265 grub_error (GRUB_ERR_BAD_DEVICE
, "no such partition");
279 pc_partition_map_get_name (const grub_partition_t p
)
282 struct grub_pc_partition
*pcdata
= p
->data
;
284 name
= grub_malloc (13);
288 if (pcdata
->bsd_part
< 0)
289 grub_sprintf (name
, "%d", pcdata
->dos_part
+ 1);
290 else if (pcdata
->dos_part
< 0)
291 grub_sprintf (name
, "%c", pcdata
->bsd_part
+ 'a');
293 grub_sprintf (name
, "%d,%c", pcdata
->dos_part
+ 1, pcdata
->bsd_part
+ 'a');
299 /* Partition map type. */
300 static struct grub_partition_map grub_pc_partition_map
=
302 .name
= "pc_partition_map",
303 .iterate
= pc_partition_map_iterate
,
304 .probe
= pc_partition_map_probe
,
305 .get_name
= pc_partition_map_get_name
308 GRUB_MOD_INIT(pc_partition_map
)
310 grub_partition_map_register (&grub_pc_partition_map
);
313 GRUB_MOD_FINI(pc_partition_map
)
315 grub_partition_map_unregister (&grub_pc_partition_map
);