/* * util - Utility function library * * Partition detection functions * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/util_part.h" #define GPT_SIGNATURE 0x4546492050415254ULL /* EFI PART */ #define MBR_SIGNATURE 0x55aa #define MBR_PART_TYPE_DOS_EXT 0x05 /* DOS extended partition */ #define MBR_PART_TYPE_WIN98_EXT 0x0f /* Windows 98 extended partition */ #define MBR_PART_TYPE_LINUX_EXT 0x85 /* Linux extended partition */ #define MBR_PART_TYPE_GPT 0xee /* GPT partition */ #define MBR_EXT_PART_NUM_FIRST 5 /* Partition number for first logical vol */ /* * MBR/MSDOS partition entry */ struct mbr_part_entry { uint8_t status; uint8_t chs_start[3]; uint8_t type; uint8_t chs_end[3]; uint32_t blk_start; uint32_t blk_cnt; } __attribute__((packed)); /* * Master Boot Record (MBR) */ struct mbr { uint8_t reserved[0x1be]; struct mbr_part_entry part_entry_vec[4]; uint16_t signature; } __attribute__((packed)); /* * GUID Partition Table (GPT) header */ struct gpt { uint64_t signature; uint32_t version; uint32_t hdr_size; uint32_t hdr_crc; uint32_t reserved1; uint64_t blk_cur; uint64_t blk_back; uint64_t blk_first; uint64_t blk_last; uint8_t guid[16]; uint64_t part_tab_blk_start; uint32_t part_tab_cnt; uint32_t part_tab_entry_size; uint32_t part_tab_crc; } __attribute__((packed)); /* * GPT partition entry */ struct gpt_part_entry { uint8_t type[16]; uint8_t guid[16]; uint64_t blk_start; uint64_t blk_end; uint64_t attr; char name[72]; } __attribute__((packed)); /* * Check for extended partition */ static int mbr_part_is_ext(uint8_t type) { if ((type == MBR_PART_TYPE_DOS_EXT) || (type == MBR_PART_TYPE_WIN98_EXT) || (type == MBR_PART_TYPE_LINUX_EXT)) return 1; return 0; } /* * Check if disk has a classic MBR partion table */ static int mbr_table_valid(struct mbr *mbr) { return mbr->signature == MBR_SIGNATURE; } /* * Search partition in logical volumes of an extended partition */ static int mbr_table_ext_search(int fh, size_t blk_start_mbr, size_t blk_start, size_t blk_cnt, size_t blk_size, int part_num) { size_t start, cnt, start_next; struct mbr mbr; /* Read MBR for logical volume */ if (lseek(fh, blk_start_mbr * blk_size, SEEK_SET) == (off_t)-1) return -1; if (read(fh, &mbr, sizeof(mbr)) == -1) return -1; /* Check for invalid MBR or last entry */ if (mbr.signature != MBR_SIGNATURE) return -1; if (mbr.part_entry_vec[0].blk_start == 0) return -1; /* First entry contains a relative offset for current logical volume */ start = blk_start_mbr + le32toh(mbr.part_entry_vec[0].blk_start); cnt = le32toh(mbr.part_entry_vec[0].blk_cnt); if ((start <= blk_start) && (cnt >= (blk_start - start) + blk_cnt)) return part_num; /* Second entry contains relative offset for next logical volume */ start_next = le32toh(mbr.part_entry_vec[1].blk_start); if (start_next == 0) return 0; start_next += blk_start_mbr; /* Recursively search for next logical volume in chain */ return mbr_table_ext_search(fh, start_next, blk_start, blk_cnt, blk_size, part_num + 1); } /* * Search partition in MBR partition table */ static int mbr_table_search(int fh, struct mbr *mbr, size_t blk_start, size_t blk_cnt, size_t blk_size, int *part_ext) { int part_num_ext, part_num; size_t start, cnt; uint8_t type; for (part_num = 1; part_num <= 4; part_num++) { type = mbr->part_entry_vec[part_num - 1].type; start = le32toh(mbr->part_entry_vec[part_num - 1].blk_start); cnt = le32toh(mbr->part_entry_vec[part_num - 1].blk_cnt); if (start == 0) /* Empty slot */ continue; /* * The kernel sets count for extended partitions explicitly. * Therefore we do not check count here. */ if (mbr_part_is_ext(type) && (start <= blk_start)) { *part_ext = 1; return part_num; } if ((start <= blk_start) && (cnt >= (blk_start - start) + blk_cnt)) return part_num; if (!mbr_part_is_ext(type)) continue; part_num_ext = mbr_table_ext_search(fh, start, blk_start, blk_cnt, blk_size, MBR_EXT_PART_NUM_FIRST); if (part_num_ext != 0) return part_num_ext; } return 0; } /* * Search partition in GPT partition table */ static int gpt_table_search(int fh, struct gpt *gpt, size_t blk_start, size_t blk_cnt, size_t blk_size) { size_t start, end, part_tab_blk_start, blk_end; uint32_t part_tab_cnt, part_tab_entry_size; struct gpt_part_entry *part_entry; unsigned int part_num; blk_end = blk_start + blk_cnt - 1; part_tab_entry_size = le32toh(gpt->part_tab_entry_size); part_tab_cnt = le32toh(gpt->part_tab_cnt); part_tab_blk_start = le64toh(gpt->part_tab_blk_start); if (lseek(fh, part_tab_blk_start * blk_size, SEEK_SET) == (off_t)-1) return -1; for (part_num = 1; part_num <= part_tab_cnt; part_num++) { char buf[part_tab_entry_size]; part_entry = (struct gpt_part_entry *) buf; if (read(fh, buf, sizeof(buf)) == -1) return -1; start = le64toh(part_entry->blk_start); end = le64toh(part_entry->blk_end); if (start == 0) /* Empty slot */ continue; if ((start <= blk_start) && (end >= blk_end)) return part_num; } return 0; } /* * Check if disk has a GPT partition table */ static int gpt_table_valid(struct gpt *gpt, struct mbr *mbr) { int cnt, part_num; uint32_t start; uint8_t type; if (gpt->signature != GPT_SIGNATURE) return 0; /* Check for protective MBR (one reserved GPT partition) */ for (part_num = 1, cnt = 0; part_num <= 4; part_num++) { start = le32toh(mbr->part_entry_vec[part_num - 1].blk_start); type = mbr->part_entry_vec[part_num - 1].type; if (!start) continue; if (type != MBR_PART_TYPE_GPT) return 0; if (++cnt > 1) return 0; } return 1; } /* * Search for partition with given start block and count * * Return partition number when found, 0 when not found, and on error -1 * Set "part_ext" to 1 for extended partitions otherwise to 0. */ int util_part_search_fh(int fh, size_t blk_start, size_t blk_cnt, size_t blk_size, int *part_ext) { struct gpt gpt; struct mbr mbr; if (lseek(fh, 0, SEEK_SET) == (off_t)-1) return -1; if (read(fh, &mbr, sizeof(mbr)) == -1) return -1; if (lseek(fh, blk_size, SEEK_SET) == (off_t)-1) return -1; if (read(fh, &gpt, sizeof(gpt)) == -1) return -1; *part_ext = 0; if (gpt_table_valid(&gpt, &mbr)) return gpt_table_search(fh, &gpt, blk_start, blk_cnt, blk_size); if (mbr_table_valid(&mbr)) return mbr_table_search(fh, &mbr, blk_start, blk_cnt, blk_size, part_ext); return -1; } /* * Search for partition with given start block and count * * Return partition number when found, 0 when not found, and on error -1 * Set "part_ext" to 1 for extended partitions otherwise to 0. */ int util_part_search(const char *device, size_t blk_start, size_t blk_cnt, size_t blk_size, int *part_ext) { int rc, fh; fh = open(device, O_RDONLY); if (fh == -1) return -1; rc = util_part_search_fh(fh, blk_start, blk_cnt, blk_size, part_ext); close(fh); return rc; }