mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
289 lines
7.1 KiB
C
289 lines
7.1 KiB
C
/*
|
|
* 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 <endian.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#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_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_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;
|
|
}
|