]> err.no Git - util-linux/commitdiff
fdisk: add GPT detection code
authorKarel Zak <kzak@redhat.com>
Thu, 31 May 2007 12:31:51 +0000 (14:31 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 5 Jun 2007 08:40:14 +0000 (10:40 +0200)
The GPT (GUID Partition Table) is unsupported by fdisk, sfdisk and
cfdisk. Unfortunately, the fdisk doesn't complain about GPT.. that's
dangerous, because user is able to blindly edit PT with unexpected
results.

Signed-off-by: Karel Zak <kzak@redhat.com>
fdisk/Makefile.am
fdisk/cfdisk.c
fdisk/fdisk.8
fdisk/fdisk.c
fdisk/gpt.c [new file with mode: 0644]
fdisk/gpt.h [new file with mode: 0644]
fdisk/sfdisk.8
fdisk/sfdisk.c

index 1e4ad02d290381fba59b723f471ed1942e5146f7..c407bbfda8c4fdfffd113a2298328077dc7ddfb4 100644 (file)
@@ -2,7 +2,7 @@ include $(top_srcdir)/config/include-Makefile.am
 
 EXTRA_DIST = README.fdisk README.cfdisk sfdisk.examples partitiontype.c
 
-fdisk_common = disksize.c i386_sys_types.c common.h
+fdisk_common = disksize.c i386_sys_types.c common.h gpt.c gpt.h
 
 if !M68K
 
index cd0f68648f61dea4383c5c6b28a84700979f0774..40b234608cbb803c8b80f8fe13c6546495750869 100644 (file)
@@ -83,6 +83,7 @@
 #include "nls.h"
 #include "xstrncpy.h"
 #include "common.h"
+#include "gpt.h"
 
 #define DEFAULT_DEVICE "/dev/hda"
 #define ALTERNATE_DEVICE "/dev/sda"
@@ -1669,6 +1670,13 @@ fill_p_info(void) {
         opentype = O_RDWR;
     opened = TRUE;
 
+    if (gpt_probe_signature_devname(fd)) {
+        print_warning(_("Warning!!  Unsupported GPT (GUID Partition Table) detected. Use GNU Parted."));
+        refresh();
+        getch();
+        clear_warning();
+    }
+
     /* Blocks are visible in more than one way:
        e.g. as block on /dev/hda and as block on /dev/hda3
        By a bug in the Linux buffer cache, we will see the old
index a5f776959c42eb882a1bf1be55ce6976ff665a62..7b487051e2027391240cb4c2ab530eeccfd80608 100644 (file)
@@ -42,6 +42,11 @@ is a menu driven program for creation and manipulation of
 partition tables.
 It understands DOS type partition tables and BSD or SUN type disklabels.
 
+.B fdisk
+doesn't understand GUID Partition Table (GPT) and
+it is not designed for large partitions. In particular case use more advanced GNU
+.B parted(8).
+
 The
 .I device
 is usually one of the following:
index c4f6458c33e6a17aa5b5aae3b3b286d12fbd3229..ecaac90ebcf13be6c1d73fced610e2d1b0621509 100644 (file)
@@ -34,6 +34,8 @@
 #include <linux/blkpg.h>
 #endif
 
+#include "gpt.h"
+
 static void delete_partition(int i);
 
 #define hex_val(c)     ({ \
@@ -2371,6 +2373,14 @@ is_ide_cdrom_or_tape(char *device) {
        return is_ide;
 }
 
+static void
+gpt_warning(char *dev)
+{
+       if (dev && gpt_probe_signature_devname(dev))
+               fprintf(stderr, _("\nWARNING: GPT (GUID Partition Table) detected on '%s'! "
+                       "The util fdisk doesn't support GPT. Use GNU Parted.\n\n"), dev);
+}
+
 static void
 try(char *device, int user_specified) {
        int gb;
@@ -2381,6 +2391,7 @@ try(char *device, int user_specified) {
        if (!user_specified)
                if (is_ide_cdrom_or_tape(device))
                        return;
+       gpt_warning(device);
        if ((fd = open(disk_device, type_open)) >= 0) {
                gb = get_boot(try_only);
                if (gb > 0) { /* I/O error */
@@ -2440,6 +2451,8 @@ unknown_command(int c) {
        printf(_("%c: unknown command\n"), c);
 }
 
+
+
 int
 main(int argc, char **argv) {
        int j, c;
@@ -2544,6 +2557,7 @@ main(int argc, char **argv) {
 
                for (j = optind; j < argc; j++) {
                        disk_device = argv[j];
+                       gpt_warning(disk_device);
                        if ((fd = open(disk_device, type_open)) < 0)
                                fatal(unable_to_open);
                        if (disksize(fd, &size))
@@ -2564,6 +2578,7 @@ main(int argc, char **argv) {
        else
                fatal(usage2);
 
+       gpt_warning(disk_device);
        get_boot(fdisk);
 
        if (osf_label) {
diff --git a/fdisk/gpt.c b/fdisk/gpt.c
new file mode 100644 (file)
index 0000000..f1d751a
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ *
+ * 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/ioctl.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "gpt.h"
+
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#define SECTOR_SIZE    512     /* default */
+
+#define _GET_BYTE(x, n)                ( ((x) >> (8 * (n))) & 0xff )
+
+#define _PED_SWAP64(x)         ( (_GET_BYTE(x, 0) << 56)       \
+                               + (_GET_BYTE(x, 1) << 48)       \
+                               + (_GET_BYTE(x, 2) << 40)       \
+                               + (_GET_BYTE(x, 3) << 32)       \
+                               + (_GET_BYTE(x, 4) << 24)       \
+                               + (_GET_BYTE(x, 5) << 16)       \
+                               + (_GET_BYTE(x, 6) << 8)        \
+                               + (_GET_BYTE(x, 7) << 0) )
+
+#define PED_SWAP64(x)           ((uint64_t) _PED_SWAP64( (uint64_t) (x) ))
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define CPU_TO_LE64(x)        (x)
+#else
+# define CPU_TO_LE64(x)        PED_SWAP64(x)
+#endif
+
+#define BLKSSZGET  _IO(0x12,104) /* get block device sector size */
+#define BLKGETLASTSECT  _IO(0x12,108)   /* get last sector of block device */
+#define BLKGETSIZE _IO(0x12,96)        /* return device size */
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)     /* return device size in bytes (u64 *arg) */
+
+#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;
+
+struct blkdev_ioctl_param {
+        unsigned int block;
+        size_t content_length;
+        char * block_contents;
+};
+
+static int
+_get_linux_version (void)
+{
+       static int kver = -1;
+       struct utsname uts;
+       int major;
+       int minor;
+       int teeny;
+
+       if (kver != -1)
+               return kver;
+       if (uname (&uts))
+               return kver = 0;
+       if (sscanf (uts.release, "%u.%u.%u", &major, &minor, &teeny) != 3)
+               return kver = 0;
+       return kver = KERNEL_VERSION (major, minor, teeny);
+}
+
+static unsigned int
+_get_sector_size (int fd)
+{
+       unsigned int sector_size;
+
+       if (_get_linux_version() < KERNEL_VERSION (2,3,0))
+               return SECTOR_SIZE;
+       if (ioctl (fd, BLKSSZGET, &sector_size))
+               return SECTOR_SIZE;
+       return sector_size;
+}
+
+static uint64_t
+_get_num_sectors(int fd)
+{
+       int version = _get_linux_version();
+       unsigned long   size;
+       uint64_t bytes=0;
+
+       if (version >= KERNEL_VERSION(2,5,4) ||
+               (version <  KERNEL_VERSION(2,5,0) &&
+                version >= KERNEL_VERSION (2,4,18)))
+       {
+                if (ioctl(fd, BLKGETSIZE64, &bytes) == 0)
+                        return bytes / _get_sector_size(fd);
+       }
+       if (ioctl (fd, BLKGETSIZE, &size))
+               return 0;
+       return size;
+}
+
+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: %s\n",
+                       strerror(errno));
+               return 0;
+       }
+       if (S_ISBLK(s.st_mode))
+               sectors = _get_num_sectors(fd);
+       else
+       {
+               fprintf(stderr,
+                       "last_lba(): I don't know how to handle files with mode %x\n",
+                       s.st_mode);
+               sectors = 1;
+       }
+       return sectors - 1;
+}
+
+static ssize_t
+read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count)
+{
+        int rc;
+        struct blkdev_ioctl_param ioctl_param;
+
+        if (!buffer) return 0;
+
+        ioctl_param.block = 0; /* read the last sector */
+        ioctl_param.content_length = count;
+        ioctl_param.block_contents = buffer;
+
+        rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param);
+        if (rc == -1) perror("read failed");
+
+        return !rc;
+}
+
+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;
+        ssize_t bytesread;
+
+       lseek(fd, offset, SEEK_SET);
+       bytesread = read(fd, buffer, bytes);
+
+        /* Kludge.  This is necessary to read/write the last
+           block of an odd-sized disk, until Linux 2.5.x kernel fixes.
+           This is only used by gpt.c, and only to read
+           one sector, so we don't have to be fancy.
+        */
+        if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd))
+                bytesread = read_lastoddsector(fd, lba, buffer, bytes);
+        return bytesread;
+}
+
+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)
+{
+       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
diff --git a/fdisk/gpt.h b/fdisk/gpt.h
new file mode 100644 (file)
index 0000000..e57c24a
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef FDISK_GPT_H
+#define FDISK_GPT_H
+
+extern int gpt_probe_signature_fd(int fd);
+extern int gpt_probe_signature_devname(char *devname);
+
+#endif /* FDISK_GPT_H */
+
index 4e1b5a04215c0fa41c7ad0def79d1604568b4c3f..37052ab4bf42f856aee3ac13f21cab668be79f03 100644 (file)
@@ -18,6 +18,11 @@ has four (main) uses: list the size of a partition, list the partitions
 on a device, check the partitions on a device, and - very dangerous -
 repartition a device.
 
+.B sfdisk
+doesn't understand GUID Partition Table (GPT) and
+it is not designed for large partitions. In particular case use more advanced GNU
+.B parted(8).
+
 .SS "List Sizes"
 .BI "sfdisk \-s " partition
 gives the size of
index d2155357590e366729ad12a95e78f4ac0069ecc1..79e9c20eeae47891a01bf3e938268c3b273c0fc0 100644 (file)
@@ -50,6 +50,8 @@
 #include "nls.h"
 #include "common.h"
 
+#include "gpt.h"
+
 #define SIZE(a)        (sizeof(a)/sizeof(a[0]))
 
 /*
@@ -2444,6 +2446,23 @@ nextproc(void) {
        return NULL;
 }      
 
+static void
+gpt_warning(char *dev, int warn_only)
+{
+       if (force)
+               warn_only = 1;
+
+       if (dev && gpt_probe_signature_devname(dev)) {
+               fflush(stdout);
+               fprintf(stderr, _("\nWARNING: GPT (GUID Partition Table) detected on '%s'! "
+                       "The util sfdisk doesn't support GPT. Use GNU Parted.\n\n"), dev);
+               if (!warn_only) {
+                       fprintf(stderr, _("Use the --force flag to overrule this check.\n"));
+                       exit(1);
+               }
+       }
+}
+
 static void do_list(char *dev, int silent);
 static void do_size(char *dev, int silent);
 static void do_geom(char *dev, int silent);
@@ -2589,6 +2608,7 @@ main(int argc, char **argv) {
        while ((dev = nextproc()) != NULL) {
            if (is_ide_cdrom_or_tape(dev))
                continue;
+           gpt_warning(dev, 1);
            if (opt_out_geom)
                do_geom(dev, 1);
            if (opt_out_pt_geom)
@@ -2616,6 +2636,7 @@ main(int argc, char **argv) {
 
     if (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify) {
        while (optind < argc) {
+           gpt_warning(argv[optind], 1);
            if (opt_out_geom)
              do_geom(argv[optind], 0);
            if (opt_out_pt_geom)
@@ -2629,6 +2650,9 @@ main(int argc, char **argv) {
        exit(exit_status);
     }
 
+    if (optind != argc-1)
+       gpt_warning(argv[optind], 0);
+
     if (activate) {
        do_activate(argv+optind, argc-optind, activatearg);
        exit(exit_status);
@@ -2648,7 +2672,7 @@ main(int argc, char **argv) {
                     (optind == argc-2) ? 0 : argv[optind+2]);
        exit(exit_status);
     }
-      
+
     if (optind != argc-1)
       fatal(_("can specify only one device (except with -l or -s)\n"));
     dev = argv[optind];