diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-11-02 20:15:39 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-11-02 20:15:39 +0400 |
commit | b13154de3eca5ba28fbb4854d916cd0be5febeed (patch) | |
tree | 30f2e9e89ab71a2df837076ac68c3ba770230294 /fdisks/gpt.c | |
download | util-linux-b13154de3eca5ba28fbb4854d916cd0be5febeed.tar.gz |
Imported Upstream version 2.22upstream/2.22upstream
Diffstat (limited to 'fdisks/gpt.c')
-rw-r--r-- | fdisks/gpt.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/fdisks/gpt.c b/fdisks/gpt.c new file mode 100644 index 0000000..bb6911a --- /dev/null +++ b/fdisks/gpt.c @@ -0,0 +1,216 @@ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * + * GPT (GUID Partition Table) signature detection. Based on libparted and + * util-linux/partx. + * + * Warning: this code doesn't do all GPT checks (CRC32, Protective MBR, ..). + * It's really GPT signature detection only. + * + * Copyright (C) 2007 Karel Zak <kzak@redhat.com> + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include "gpt.h" +#include "blkdev.h" +#include "bitops.h" +#include "closestream.h" + +#define GPT_HEADER_SIGNATURE 0x5452415020494645LL +#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 + +typedef struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} /* __attribute__ ((packed)) */ efi_guid_t; +/* commented out "__attribute__ ((packed))" to work around gcc bug (fixed + * in gcc3.1): __attribute__ ((packed)) breaks addressing on initialized + * data. It turns out we don't need it in this case, so it doesn't break + * anything :) + */ + +typedef struct _GuidPartitionTableHeader_t { + uint64_t Signature; + uint32_t Revision; + uint32_t HeaderSize; + uint32_t HeaderCRC32; + uint32_t Reserved1; + uint64_t MyLBA; + uint64_t AlternateLBA; + uint64_t FirstUsableLBA; + uint64_t LastUsableLBA; + efi_guid_t DiskGUID; + uint64_t PartitionEntryLBA; + uint32_t NumberOfPartitionEntries; + uint32_t SizeOfPartitionEntry; + uint32_t PartitionEntryArrayCRC32; + uint8_t Reserved2[512 - 92]; +} __attribute__ ((packed)) GuidPartitionTableHeader_t; + +static int +_get_sector_size (int fd) +{ + int sector_size; + + if (blkdev_get_sector_size(fd, §or_size) == -1) + return DEFAULT_SECTOR_SIZE; + return sector_size; +} + +static uint64_t +_get_num_sectors(int fd) +{ + unsigned long long bytes=0; + + if (blkdev_get_size(fd, &bytes) == -1) + return 0; + return bytes / _get_sector_size(fd); +} + +static uint64_t +last_lba(int fd) +{ + int rc; + uint64_t sectors = 0; + struct stat s; + + memset(&s, 0, sizeof (s)); + rc = fstat(fd, &s); + if (rc == -1) + { + fprintf(stderr, "last_lba() could not stat: %m\n"); + return 0; + } + if (S_ISBLK(s.st_mode)) + sectors = _get_num_sectors(fd); + else if (S_ISREG(s.st_mode)) + sectors = s.st_size >> _get_sector_size(fd); + else + { + fprintf(stderr, + "last_lba(): I don't know how to handle files with mode %o\n", + s.st_mode); + sectors = 1; + } + return sectors - 1; +} + +static ssize_t +read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) +{ + int sector_size = _get_sector_size(fd); + off_t offset = lba * sector_size; + + lseek(fd, offset, SEEK_SET); + return read(fd, buffer, bytes); +} + +static GuidPartitionTableHeader_t * +alloc_read_gpt_header(int fd, uint64_t lba) +{ + GuidPartitionTableHeader_t *gpt = + (GuidPartitionTableHeader_t *) malloc(sizeof (GuidPartitionTableHeader_t)); + if (!gpt) + return NULL; + memset(gpt, 0, sizeof (*gpt)); + if (!read_lba(fd, lba, gpt, sizeof (GuidPartitionTableHeader_t))) + { + free(gpt); + return NULL; + } + return gpt; +} + +static int +gpt_check_signature(int fd, uint64_t lba) +{ + GuidPartitionTableHeader_t *gpt; + int res=0; + + if ((gpt = alloc_read_gpt_header(fd, lba))) + { + if (gpt->Signature == cpu_to_le64(GPT_HEADER_SIGNATURE)) + res = 1; + free(gpt); + } + return res; +} + +/* returns: + * 0 not found GPT + * 1 for valid primary GPT header + * 2 for valid alternative GPT header + */ +int +gpt_probe_signature_fd(int fd) +{ + int res = 0; + + /* check primary GPT header */ + if (gpt_check_signature(fd, GPT_PRIMARY_PARTITION_TABLE_LBA)) + res = 1; + else + { + /* check alternative GPT header */ + uint64_t lastlba = last_lba(fd); + if (gpt_check_signature(fd, lastlba)) + res = 2; + } + return res; +} + +int +gpt_probe_signature_devname(char *devname) +{ + int res, fd; + if ((fd = open(devname, O_RDONLY)) < 0) + return 0; + res = gpt_probe_signature_fd(fd); + close(fd); + return res; +} + +#ifdef GPT_TEST_MAIN +int +main(int argc, char **argv) +{ + atexit(close_stdout); + if (argc!=2) + { + fprintf(stderr, "usage: %s <dev>\n", argv[0]); + exit(EXIT_FAILURE); + } + if (gpt_probe_signature_devname(argv[1])) + printf("GPT (GUID Partition Table) detected on %s\n", argv[1]); + exit(EXIT_SUCCESS); +} +#endif |