| /* ----------------------------------------------------------------------- * |
| * |
| * Copyright 2009 Pierre-Alexandre Meyer |
| * |
| * Some parts borrowed from chain.c32: |
| * |
| * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved |
| * Copyright 2009 Intel Corporation; author: H. Peter Anvin |
| * |
| * This file is part of Syslinux, and is made available under |
| * the terms of the GNU General Public License version 2. |
| * |
| * ----------------------------------------------------------------------- */ |
| |
| #include <stdlib.h> |
| |
| #include <disk/common.h> |
| #include <disk/geom.h> |
| #include <disk/msdos.h> |
| #include <disk/partition.h> |
| #include <disk/read.h> |
| |
| static int is_extended_partition(struct part_entry *ptab) |
| { |
| return (ptab->ostype == 0x05 || |
| ptab->ostype == 0x0f || ptab->ostype == 0x85); |
| } |
| |
| static int msdos_magic_present(const char *ptab) |
| { |
| return (*(uint16_t *) (ptab + 0x1fe) == 0xaa55); |
| } |
| |
| /** |
| * process_extended_partition - execute a callback for each partition contained listed in an ebr |
| * @drive_info: driveinfo struct describing the drive |
| * @partition_offset: Absolute start (lba) of the extended partition |
| * @ebr_offset: Relative start (lba) of the current ebr processed within |
| * the extended partition |
| * @callback: Callback to execute |
| * @error: Buffer for I/O errors |
| * @nb_part_seen: Number of partitions found on the disk so far |
| **/ |
| static int process_extended_partition(struct driveinfo *drive_info, |
| const int partition_offset, |
| const int ebr_offset, |
| p_callback callback, int nb_part_seen) |
| { |
| int status = 0; |
| /* The ebr is located at the first sector of the extended partition */ |
| char *ebr = malloc(SECTOR * sizeof(char)); |
| |
| if (read_sectors(drive_info, ebr, partition_offset + ebr_offset, 1) == -1) |
| goto abort; |
| |
| /* Check msdos magic signature */ |
| if (!msdos_magic_present(ebr)) |
| goto abort; |
| |
| struct part_entry *ptab = |
| (struct part_entry *)(ebr + PARTITION_TABLES_OFFSET); |
| |
| for (int i = 0; i < 4; i++) { |
| if (status == -1) |
| goto abort; |
| |
| if (!is_extended_partition(&ptab[i])) { |
| /* |
| * This EBR partition table entry points to the |
| * logical partition associated to that EBR |
| */ |
| int logical_partition_start = ebr_offset + ptab[i].start_lba; |
| |
| /* Last EBR in the extended partition? */ |
| if (!logical_partition_start) |
| continue; |
| |
| /* |
| * Check for garbage: |
| * 3rd and 4th entries in an EBR should be zero |
| * Some (malformed) partitioning software still add some |
| * data partitions there. |
| */ |
| if (ptab[i].start_lba <= 0 || ptab[i].length <= 0) |
| continue; |
| |
| nb_part_seen++; |
| callback(drive_info, |
| &ptab[i], |
| partition_offset + logical_partition_start, nb_part_seen); |
| } else |
| status = process_extended_partition(drive_info, |
| partition_offset, |
| ptab[i].start_lba, |
| callback, nb_part_seen); |
| } |
| |
| free(ebr); |
| return 0; |
| |
| abort: |
| free(ebr); |
| return -1; |
| } |
| |
| /** |
| * process_mbr - execute a callback for each partition contained in an {m,e}br |
| * @drive_info: driveinfo struct describing the drive |
| * @ptab: Pointer to the partition table |
| * @callback: Callback to execute |
| **/ |
| static int process_mbr(struct driveinfo *drive_info, struct part_entry *ptab, |
| p_callback callback) |
| { |
| int status = 0; |
| |
| for (int i = 0; i < 4; i++) { |
| if (status == -1) |
| return -1; |
| |
| if (ptab[i].start_sect > 0) { |
| if (is_extended_partition(&ptab[i])) { |
| callback(drive_info, &ptab[i], ptab[i].start_lba, i + 1); |
| status = |
| process_extended_partition(drive_info, ptab[i].start_lba, 0, |
| callback, 4); |
| } else |
| callback(drive_info, &ptab[i], ptab[i].start_lba, i + 1); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * parse_partition_table - execute a callback for each partition entry |
| * @d: driveinfo struct describing the drive |
| * @callback: Callback to execute |
| * |
| * The signature of the callback should be the following: |
| * |
| * void callback(struct driveinfo *drive_info, |
| * struct part_entry *ptab, |
| * int offset_root, |
| * int nb_part_seen) |
| **/ |
| int parse_partition_table(struct driveinfo *d, p_callback callback) |
| { |
| char *mbr = malloc(SECTOR * sizeof(char)); |
| |
| if (read_mbr(d->disk, mbr) == -1) |
| return -1; |
| else { |
| /* Check msdos magic signature */ |
| if (!msdos_magic_present(mbr)) |
| return -1; |
| |
| struct part_entry *ptab = |
| (struct part_entry *)(mbr + PARTITION_TABLES_OFFSET); |
| return process_mbr(d, ptab, callback); |
| } |
| } |