OSDN Git Service

linux: clean up device naming code (no semantic change)
[android-x86/external-parted.git] / libparted / arch / linux.c
index 271bc8b..5c73a55 100644 (file)
@@ -1,5 +1,5 @@
 /* libparted - a library for manipulating disk partitions
-    Copyright (C) 1999 - 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+    Copyright (C) 1999-2011 Free Software Foundation, Inc.
 
     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
 
 #include <config.h>
 #include <arch/linux.h>
-
+#include <linux/blkpg.h>
 #include <parted/parted.h>
 #include <parted/debug.h>
+#if defined __s390__ || defined __s390x__
+#include <parted/fdasd.h>
+#endif
 
 #include <ctype.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/utsname.h>        /* for uname() */
 #include <scsi/scsi.h>
+#include <assert.h>
 #ifdef ENABLE_DEVICE_MAPPER
 #include <libdevmapper.h>
 #endif
 
-#include "blkpg.h"
 #include "../architecture.h"
 #include "dirname.h"
+#include "xstrtol.h"
 
 #if ENABLE_NLS
 #  include <libintl.h>
@@ -218,6 +222,14 @@ struct blkdev_ioctl_param {
 #define SCSI_DISK5_MAJOR        69
 #define SCSI_DISK6_MAJOR        70
 #define SCSI_DISK7_MAJOR        71
+#define SCSI_DISK8_MAJOR        128
+#define SCSI_DISK9_MAJOR        129
+#define SCSI_DISK10_MAJOR       130
+#define SCSI_DISK11_MAJOR       131
+#define SCSI_DISK12_MAJOR       132
+#define SCSI_DISK13_MAJOR       133
+#define SCSI_DISK14_MAJOR       134
+#define SCSI_DISK15_MAJOR       135
 #define COMPAQ_SMART2_MAJOR     72
 #define COMPAQ_SMART2_MAJOR1    73
 #define COMPAQ_SMART2_MAJOR2    74
@@ -247,15 +259,19 @@ struct blkdev_ioctl_param {
 #define UBD_MAJOR               98
 #define DASD_MAJOR              94
 #define VIODASD_MAJOR           112
+#define AOE_MAJOR               152
 #define SX8_MAJOR1              160
 #define SX8_MAJOR2              161
 #define XVD_MAJOR               202
 #define SDMMC_MAJOR             179
+#define LOOP_MAJOR              7
+#define MD_MAJOR                9
 
 #define SCSI_BLK_MAJOR(M) (                                             \
                 (M) == SCSI_DISK0_MAJOR                                 \
                 || (M) == SCSI_CDROM_MAJOR                              \
-                || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR))
+                || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) \
+                || ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
 
 /* Maximum number of partitions supported by linux. */
 #define MAX_NUM_PARTS          64
@@ -283,7 +299,14 @@ _read_fd (int fd, char **buf)
                         break;
                 filesize += s;
                 size += s;
-                *buf = realloc (*buf, size);
+                char *new_buf = realloc (*buf, size);
+                if (new_buf == NULL) {
+                        int saved_errno = errno;
+                        free (*buf);
+                        errno = saved_errno;
+                        return -1;
+                }
+                *buf = new_buf;
         } while (1);
 
         if (filesize == 0 && s < 0) {
@@ -291,8 +314,14 @@ _read_fd (int fd, char **buf)
                 *buf = NULL;
                 return -1;
         } else {
-                /* there is always some excess memory left unused */
-                *buf = realloc (*buf, filesize+1);
+                char *new_buf = realloc (*buf, filesize + 1);
+                if (new_buf == NULL) {
+                        int saved_errno = errno;
+                        free (*buf);
+                        errno = saved_errno;
+                        return -1;
+                }
+                *buf = new_buf;
                 (*buf)[filesize] = '\0';
         }
 
@@ -420,7 +449,8 @@ _dm_maptype (PedDevice *dev)
         if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
                 return r;
 
-        if (!dm_task_set_name(dmt, dev->path))
+        if (!dm_task_set_major_minor(dmt, arch_specific->major,
+                                     arch_specific->minor, 0))
                 goto bad;
 
         dm_task_no_open_count(dmt);
@@ -477,8 +507,8 @@ _probe_dm_devices ()
 static int
 _device_stat (PedDevice* dev, struct stat * dev_stat)
 {
-        PED_ASSERT (dev != NULL, return 0);
-        PED_ASSERT (!dev->external_mode, return 0);
+        PED_ASSERT (dev != NULL);
+        PED_ASSERT (!dev->external_mode);
 
         while (1) {
                 if (!stat (dev->path, dev_stat)) {
@@ -502,6 +532,7 @@ _device_probe_type (PedDevice* dev)
         struct stat             dev_stat;
         int                     dev_major;
         int                     dev_minor;
+        LinuxSpecific*          arch_specific = LINUX_SPECIFIC (dev);
 
         if (!_device_stat (dev, &dev_stat))
                 return 0;
@@ -511,8 +542,8 @@ _device_probe_type (PedDevice* dev)
                 return 1;
         }
 
-        dev_major = major (dev_stat.st_rdev);
-        dev_minor = minor (dev_stat.st_rdev);
+        arch_specific->major = dev_major = major (dev_stat.st_rdev);
+        arch_specific->minor = dev_minor = minor (dev_stat.st_rdev);
 
         if (SCSI_BLK_MAJOR (dev_major) && (dev_minor % 0x10 == 0)) {
                 dev->type = PED_DEVICE_SCSI;
@@ -522,6 +553,8 @@ _device_probe_type (PedDevice* dev)
                 dev->type = PED_DEVICE_DAC960;
         } else if (dev_major == ATARAID_MAJOR && (dev_minor % 0x10 == 0)) {
                 dev->type = PED_DEVICE_ATARAID;
+        } else if (dev_major == AOE_MAJOR && (dev_minor % 0x10 == 0)) {
+                dev->type = PED_DEVICE_AOE;
         } else if (dev_major == DASD_MAJOR && (dev_minor % 0x4 == 0)) {
                 dev->type = PED_DEVICE_DASD;
         } else if (dev_major == VIODASD_MAJOR && (dev_minor % 0x8 == 0)) {
@@ -551,6 +584,10 @@ _device_probe_type (PedDevice* dev)
                 dev->type = PED_DEVICE_SDMMC;
         } else if (_is_virtblk_major(dev_major)) {
                 dev->type = PED_DEVICE_VIRTBLK;
+        } else if (dev_major == LOOP_MAJOR) {
+                dev->type = PED_DEVICE_FILE;
+        } else if (dev_major == MD_MAJOR) {
+                dev->type = PED_DEVICE_MD;
         } else {
                 dev->type = PED_DEVICE_UNKNOWN;
         }
@@ -618,7 +655,7 @@ _device_set_sector_size (PedDevice* dev)
         dev->sector_size = PED_SECTOR_SIZE_DEFAULT;
         dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT;
 
-        PED_ASSERT (dev->open_count, return);
+        PED_ASSERT (dev->open_count);
 
         if (_get_linux_version() < KERNEL_VERSION (2,3,0)) {
                 dev->sector_size = PED_SECTOR_SIZE_DEFAULT;
@@ -634,38 +671,36 @@ _device_set_sector_size (PedDevice* dev)
                         dev->path, strerror (errno), PED_SECTOR_SIZE_DEFAULT);
         } else {
                 dev->sector_size = (long long)sector_size;
+                dev->phys_sector_size = dev->sector_size;
         }
 
 #if USE_BLKID
         get_blkid_topology(arch_specific);
         if (!arch_specific->topology) {
-                ped_exception_throw (
-                        PED_EXCEPTION_WARNING,
-                        PED_EXCEPTION_OK,
-                        _("Could not determine minimum io size for %s: %s.\n"
-                          "Using the default size (%lld)."),
-                        dev->path, strerror (errno), PED_SECTOR_SIZE_DEFAULT);
+                dev->phys_sector_size = 0;
         } else {
                 dev->phys_sector_size =
-                        blkid_topology_get_minimum_io_size(
+                        blkid_topology_get_physical_sector_size(
                                 arch_specific->topology);
         }
+        if (dev->phys_sector_size == 0) {
+                ped_exception_throw (
+                        PED_EXCEPTION_WARNING,
+                        PED_EXCEPTION_OK,
+                        _("Could not determine physical sector size for %s.\n"
+                          "Using the logical sector size (%lld)."),
+                        dev->path, dev->sector_size);
+                dev->phys_sector_size = dev->sector_size;
+        }
 #endif
 
+#if defined __s390__ || defined __s390x__
         /* Return PED_SECTOR_SIZE_DEFAULT for DASDs. */
         if (dev->type == PED_DEVICE_DASD) {
+                arch_specific->real_sector_size = dev->sector_size;
                 dev->sector_size = PED_SECTOR_SIZE_DEFAULT;
         }
-
-        if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) {
-                ped_exception_throw (
-                        PED_EXCEPTION_WARNING,
-                        PED_EXCEPTION_OK,
-                        _("Device %s has a logical sector size of %lld.  Not "
-                          "all parts of GNU Parted support this at the moment, "
-                          "and the working code is HIGHLY EXPERIMENTAL.\n"),
-                        dev->path, dev->sector_size);
-        }
+#endif
 }
 
 static int
@@ -686,10 +721,17 @@ _device_get_length (PedDevice* dev)
         unsigned long           size;
         LinuxSpecific*          arch_specific = LINUX_SPECIFIC (dev);
         uint64_t bytes=0;
+        const char*             test_str;
+        PedSector               test_size;
+
 
+        PED_ASSERT (dev->open_count > 0);
+        PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0);
 
-        PED_ASSERT (dev->open_count > 0, return 0);
-        PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return 0);
+        test_str = getenv ("PARTED_TEST_DEVICE_LENGTH");
+        if (test_str
+            && xstrtoll (test_str, NULL, 10, &test_size, NULL) == LONGINT_OK)
+                return test_size;
 
         if (_kernel_has_blkgetsize64()) {
                 if (ioctl(arch_specific->fd, BLKGETSIZE64, &bytes) == 0) {
@@ -719,7 +761,7 @@ _device_probe_geometry (PedDevice* dev)
 
         if (!_device_stat (dev, &dev_stat))
                 return 0;
-        PED_ASSERT (S_ISBLK (dev_stat.st_mode), return 0);
+        PED_ASSERT (S_ISBLK (dev_stat.st_mode));
 
         _device_set_sector_size (dev);
 
@@ -799,7 +841,7 @@ init_ide (PedDevice* dev)
                                 dev->model = strdup(_("Generic IDE"));
                                 break;
                         default:
-                                PED_ASSERT (0, (void) 0);
+                                PED_ASSERT (0);
                                 break;
                 }
         } else {
@@ -835,7 +877,7 @@ init_ide (PedDevice* dev)
                                 case PED_EXCEPTION_IGNORE:
                                         break;
                                 default:
-                                        PED_ASSERT (0, (void) 0);
+                                        PED_ASSERT (0);
                                         break;
                         }
                 }
@@ -1079,12 +1121,13 @@ error:
         return 0;
 }
 
+#if defined __s390__ || defined __s390x__
 static int
 init_dasd (PedDevice* dev, const char* model_name)
 {
         struct stat             dev_stat;
         struct hd_geometry      geo;
-        char *errstr = 0;
+        dasd_information_t dasd_info;
 
         if (!_device_stat (dev, &dev_stat))
                 goto error;
@@ -1094,7 +1137,7 @@ init_dasd (PedDevice* dev, const char* model_name)
 
         LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev);
 
-        PED_ASSERT (S_ISBLK (dev_stat.st_mode), return 0);
+        PED_ASSERT (S_ISBLK (dev_stat.st_mode));
 
         _device_set_sector_size (dev);
         if (!dev->sector_size)
@@ -1120,20 +1163,24 @@ init_dasd (PedDevice* dev, const char* model_name)
                 dev->hw_geom = dev->bios_geom;
         }
 
+        if (!ioctl(arch_specific->fd, BIODASDINFO, &dasd_info)) {
+                arch_specific->devno = dasd_info.devno;
+        } else {
+                arch_specific->devno = arch_specific->major * 256 +
+                                       arch_specific->minor;
+        }
+
         dev->model = strdup (model_name);
 
         ped_device_close (dev);
         return 1;
 
-        ped_exception_throw ( PED_EXCEPTION_ERROR,
-                              PED_EXCEPTION_IGNORE_CANCEL,
-                              errstr );
-
 error_close_dev:
         ped_device_close (dev);
 error:
         return 0;
 }
+#endif
 
 static int
 init_generic (PedDevice* dev, const char* model_name)
@@ -1151,6 +1198,12 @@ init_generic (PedDevice* dev, const char* model_name)
         if (_device_probe_geometry (dev)) {
                 ped_exception_leave_all ();
         } else {
+               if (!_device_get_length (dev)) {
+                       ped_exception_catch ();
+                       ped_exception_leave_all ();
+                       goto error_close_dev;
+               }
+
                 /* hack to allow use of files, for testing */
                 ped_exception_catch ();
                 ped_exception_leave_all ();
@@ -1171,7 +1224,7 @@ init_generic (PedDevice* dev, const char* model_name)
                         case PED_EXCEPTION_IGNORE:
                                 break;
                         default:
-                                PED_ASSERT (0, (void) 0);
+                                PED_ASSERT (0);
                                 break;
                 }
 
@@ -1229,7 +1282,7 @@ linux_new (const char* path)
         PedDevice*      dev;
         LinuxSpecific*  arch_specific;
 
-        PED_ASSERT (path != NULL, return NULL);
+        PED_ASSERT (path != NULL);
 
         dev = (PedDevice*) ped_malloc (sizeof (PedDevice));
         if (!dev)
@@ -1280,10 +1333,17 @@ linux_new (const char* path)
                         goto error_free_arch_specific;
                 break;
 
+        case PED_DEVICE_AOE:
+                if (!init_generic (dev, _("ATA over Ethernet Device")))
+                    goto error_free_arch_specific;
+                break;
+
+#if defined __s390__ || defined __s390x__
         case PED_DEVICE_DASD:
                 if (!init_dasd (dev, _("IBM S390 DASD drive")))
                         goto error_free_arch_specific;
                 break;
+#endif
 
         case PED_DEVICE_VIODASD:
                 if (!init_generic (dev, _("IBM iSeries Virtual DASD")))
@@ -1348,6 +1408,11 @@ linux_new (const char* path)
                         goto error_free_arch_specific;
                 break;
 
+        case PED_DEVICE_MD:
+                if (!init_generic(dev, _("Linux Software RAID Array")))
+                        goto error_free_arch_specific;
+                break;
+
         default:
                 ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
                                 PED_EXCEPTION_CANCEL,
@@ -1565,9 +1630,9 @@ _device_seek (const PedDevice* dev, PedSector sector)
 {
         LinuxSpecific*  arch_specific;
 
-        PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return 0);
-        PED_ASSERT (dev != NULL, return 0);
-        PED_ASSERT (!dev->external_mode, return 0);
+        PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0);
+        PED_ASSERT (dev != NULL);
+        PED_ASSERT (!dev->external_mode);
 
         arch_specific = LINUX_SPECIFIC (dev);
 
@@ -1589,8 +1654,8 @@ _read_lastoddsector (const PedDevice* dev, void* buffer)
         LinuxSpecific*                  arch_specific;
         struct blkdev_ioctl_param       ioctl_param;
 
-        PED_ASSERT(dev != NULL, return 0);
-        PED_ASSERT(buffer != NULL, return 0);
+        PED_ASSERT(dev != NULL);
+        PED_ASSERT(buffer != NULL);
 
         arch_specific = LINUX_SPECIFIC (dev);
 
@@ -1624,8 +1689,8 @@ linux_read (const PedDevice* dev, void* buffer, PedSector start,
         PedExceptionOption      ex_status;
         void*                   diobuf = NULL;
 
-        PED_ASSERT (dev != NULL, return 0);
-        PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return 0);
+        PED_ASSERT (dev != NULL);
+        PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0);
 
         if (_get_linux_version() < KERNEL_VERSION (2,6,0)) {
                 /* Kludge.  This is necessary to read/write the last
@@ -1659,7 +1724,7 @@ linux_read (const PedDevice* dev, void* buffer, PedSector start,
                         case PED_EXCEPTION_CANCEL:
                                 return 0;
                         default:
-                                PED_ASSERT (0, (void) 0);
+                                PED_ASSERT (0);
                                 break;
                 }
         }
@@ -1683,7 +1748,9 @@ linux_read (const PedDevice* dev, void* buffer, PedSector start,
                 ex_status = ped_exception_throw (
                         PED_EXCEPTION_ERROR,
                         PED_EXCEPTION_RETRY_IGNORE_CANCEL,
-                        _("%s during read on %s"),
+                        (status == 0
+                         ? _("end of file while reading %s")
+                         : _("%s during read on %s")),
                         strerror (errno),
                         dev->path);
 
@@ -1701,7 +1768,7 @@ linux_read (const PedDevice* dev, void* buffer, PedSector start,
                                 free(diobuf);
                                 return 0;
                         default:
-                                PED_ASSERT (0, (void) 0);
+                                PED_ASSERT (0);
                                 break;
                 }
         }
@@ -1717,8 +1784,8 @@ _write_lastoddsector (PedDevice* dev, const void* buffer)
         LinuxSpecific*                  arch_specific;
         struct blkdev_ioctl_param       ioctl_param;
 
-        PED_ASSERT(dev != NULL, return 0);
-        PED_ASSERT(buffer != NULL, return 0);
+        PED_ASSERT(dev != NULL);
+        PED_ASSERT(buffer != NULL);
 
         arch_specific = LINUX_SPECIFIC (dev);
 
@@ -1753,7 +1820,7 @@ linux_write (PedDevice* dev, const void* buffer, PedSector start,
         void*                   diobuf;
         void*                   diobuf_start;
 
-        PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return 0);
+        PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0);
 
         if (dev->read_only) {
                 if (ped_exception_throw (
@@ -1799,7 +1866,7 @@ linux_write (PedDevice* dev, const void* buffer, PedSector start,
                         case PED_EXCEPTION_CANCEL:
                                 return 0;
                         default:
-                                PED_ASSERT (0, (void) 0);
+                                PED_ASSERT (0);
                                 break;
                 }
         }
@@ -1843,7 +1910,7 @@ linux_write (PedDevice* dev, const void* buffer, PedSector start,
                                 free(diobuf_start);
                                 return 0;
                         default:
-                                PED_ASSERT (0, (void) 0);
+                                PED_ASSERT (0);
                                 break;
                 }
         }
@@ -1862,7 +1929,7 @@ linux_check (PedDevice* dev, void* buffer, PedSector start, PedSector count)
         int             status;
         void*           diobuf;
 
-        PED_ASSERT(dev != NULL, return 0);
+        PED_ASSERT(dev != NULL);
 
         if (!_device_seek (dev, start))
                 return 0;
@@ -1913,7 +1980,7 @@ _do_fsync (PedDevice* dev)
                         case PED_EXCEPTION_CANCEL:
                                 return 0;
                         default:
-                                PED_ASSERT (0, (void) 0);
+                                PED_ASSERT (0);
                                 break;
                 }
         }
@@ -1923,8 +1990,8 @@ _do_fsync (PedDevice* dev)
 static int
 linux_sync (PedDevice* dev)
 {
-        PED_ASSERT (dev != NULL, return 0);
-        PED_ASSERT (!dev->external_mode, return 0);
+        PED_ASSERT (dev != NULL);
+        PED_ASSERT (!dev->external_mode);
 
         if (dev->read_only)
                 return 1;
@@ -1937,8 +2004,8 @@ linux_sync (PedDevice* dev)
 static int
 linux_sync_fast (PedDevice* dev)
 {
-        PED_ASSERT (dev != NULL, return 0);
-        PED_ASSERT (!dev->external_mode, return 0);
+        PED_ASSERT (dev != NULL);
+        PED_ASSERT (!dev->external_mode);
 
         if (dev->read_only)
                 return 1;
@@ -2128,32 +2195,39 @@ linux_probe_all ()
                 _probe_proc_partitions ();
 }
 
-static char*
-_device_get_part_path (PedDevice* dev, int num)
+static char * _GL_ATTRIBUTE_FORMAT ((__printf__, 1, 2))
+zasprintf (const char *format, ...)
 {
-        int             path_len = strlen (dev->path);
-        int             result_len = path_len + 16;
-        char*           result;
+  va_list args;
+  char *resultp;
+  va_start (args, format);
+  int r = vasprintf (&resultp, format, args);
+  va_end (args);
+  return r < 0 ? NULL : resultp;
+}
 
-        result = (char*) ped_malloc (result_len);
-        if (!result)
-                return NULL;
+static char*
+_device_get_part_path (PedDevice *dev, int num)
+{
+        size_t path_len = strlen (dev->path);
 
+        char *result;
         /* Check for devfs-style /disc => /partN transformation
            unconditionally; the system might be using udev with devfs rules,
            and if not the test is harmless. */
-        if (!strcmp (dev->path + path_len - 5, "/disc")) {
+        if (5 < path_len && !strcmp (dev->path + path_len - 5, "/disc")) {
                 /* replace /disc with /path%d */
-                strcpy (result, dev->path);
-                snprintf (result + path_len - 5, 16, "/part%d", num);
-        } else if (dev->type == PED_DEVICE_DAC960
-                        || dev->type == PED_DEVICE_CPQARRAY
-                        || dev->type == PED_DEVICE_ATARAID
-                        || dev->type == PED_DEVICE_DM
-                        || isdigit (dev->path[path_len - 1]))
-                snprintf (result, result_len, "%sp%d", dev->path, num);
-        else
-                snprintf (result, result_len, "%s%d", dev->path, num);
+                result = zasprintf ("%.*s/part%d",
+                                    (int) (path_len - 5), dev->path, num);
+        } else {
+                char const *p = (dev->type == PED_DEVICE_DAC960
+                                 || dev->type == PED_DEVICE_CPQARRAY
+                                 || dev->type == PED_DEVICE_ATARAID
+                                 || dev->type == PED_DEVICE_DM
+                                 || isdigit (dev->path[path_len - 1])
+                                 ? "p" : "");
+                result = zasprintf ("%s%s%d", dev->path, p, num);
+        }
 
         return result;
 }
@@ -2235,7 +2309,7 @@ _partition_is_mounted (const PedPartition *part)
 static int
 _has_partitions (const PedDisk* disk)
 {
-        PED_ASSERT(disk != NULL, return 0);
+        PED_ASSERT(disk != NULL);
 
         /* Some devices can't be partitioned. */
         if (!strcmp (disk->type->name, "loop"))
@@ -2249,7 +2323,7 @@ linux_partition_is_busy (const PedPartition* part)
 {
         PedPartition*   walk;
 
-        PED_ASSERT (part != NULL, return 0);
+        PED_ASSERT (part != NULL);
 
         if (_partition_is_mounted (part))
                 return 1;
@@ -2262,6 +2336,267 @@ linux_partition_is_busy (const PedPartition* part)
         return 0;
 }
 
+static int
+_blkpg_part_command (PedDevice* dev, struct blkpg_partition* part, int op)
+{
+        LinuxSpecific*          arch_specific = LINUX_SPECIFIC (dev);
+        struct blkpg_ioctl_arg  ioctl_arg;
+
+        ioctl_arg.op = op;
+        ioctl_arg.flags = 0;
+        ioctl_arg.datalen = sizeof (struct blkpg_partition);
+        ioctl_arg.data = (void*) part;
+
+        return ioctl (arch_specific->fd, BLKPG, &ioctl_arg) == 0;
+}
+
+static int
+_blkpg_add_partition (PedDisk* disk, const PedPartition *part)
+{
+        struct blkpg_partition  linux_part;
+        const char*             vol_name;
+        char*                   dev_name;
+
+        PED_ASSERT(disk != NULL);
+        PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0);
+
+        if (!_has_partitions (disk))
+                return 0;
+
+        if (ped_disk_type_check_feature (disk->type,
+                                         PED_DISK_TYPE_PARTITION_NAME))
+                vol_name = ped_partition_get_name (part);
+        else
+                vol_name = NULL;
+
+        dev_name = _device_get_part_path (disk->dev, part->num);
+        if (!dev_name)
+                return 0;
+
+        memset (&linux_part, 0, sizeof (linux_part));
+        linux_part.start = part->geom.start * disk->dev->sector_size;
+        /* see fs/partitions/msdos.c:msdos_partition(): "leave room for LILO" */
+        if (part->type & PED_PARTITION_EXTENDED)
+                linux_part.length = part->geom.length == 1 ? 512 : 1024;
+        else
+                linux_part.length = part->geom.length * disk->dev->sector_size;
+        linux_part.pno = part->num;
+        strncpy (linux_part.devname, dev_name, BLKPG_DEVNAMELTH);
+        if (vol_name)
+                strncpy (linux_part.volname, vol_name, BLKPG_VOLNAMELTH);
+
+        free (dev_name);
+
+        if (!_blkpg_part_command (disk->dev, &linux_part,
+                                  BLKPG_ADD_PARTITION)) {
+                return ped_exception_throw (
+                        PED_EXCEPTION_ERROR,
+                        PED_EXCEPTION_IGNORE_CANCEL,
+                        _("Error informing the kernel about modifications to "
+                          "partition %s -- %s.  This means Linux won't know "
+                          "about any changes you made to %s until you reboot "
+                          "-- so you shouldn't mount it or use it in any way "
+                          "before rebooting."),
+                        linux_part.devname,
+                        strerror (errno),
+                        linux_part.devname)
+                                == PED_EXCEPTION_IGNORE;
+        }
+
+        return 1;
+}
+
+static int
+_blkpg_remove_partition (PedDisk* disk, int n)
+{
+        struct blkpg_partition  linux_part;
+
+        if (!_has_partitions (disk))
+                return 0;
+
+        memset (&linux_part, 0, sizeof (linux_part));
+        linux_part.pno = n;
+        return _blkpg_part_command (disk->dev, &linux_part,
+                                    BLKPG_DEL_PARTITION);
+}
+
+/*
+ * The number of partitions that a device can have depends on the kernel.
+ * If we don't find this value in /sys/block/DEV/range, we will use our own
+ * value.
+ */
+static unsigned int
+_device_get_partition_range(PedDevice* dev)
+{
+        int         range, r;
+        char        path[128];
+        FILE*       fp;
+        bool        ok;
+
+        r = snprintf(path, sizeof(path), "/sys/block/%s/range",
+                     last_component(dev->path));
+        if (r < 0 || r >= sizeof(path))
+                return MAX_NUM_PARTS;
+
+        fp = fopen(path, "r");
+        if (!fp)
+                return MAX_NUM_PARTS;
+
+        ok = fscanf(fp, "%d", &range) == 1;
+        fclose(fp);
+
+        /* (range <= 0) is none sense.*/
+        return ok && range > 0 ? range : MAX_NUM_PARTS;
+}
+
+/*
+ * Sync the partition table in two step process:
+ * 1. Remove all of the partitions from the kernel's tables, but do not attempt
+ *    removal of any partition for which the corresponding ioctl call fails.
+ * 2. Add all the partitions that we hold in disk, throwing a warning
+ *    if we cannot because step 1 failed to remove it and it is not being
+ *    added back with the same start and length.
+ *
+ * To achieve this two step process we must calculate the minimum number of
+ * maximum possible partitions between what linux supports and what the label
+ * type supports. EX:
+ *
+ * number=MIN(max_parts_supported_in_linux,max_parts_supported_in_msdos_tables)
+ */
+static int
+_disk_sync_part_table (PedDisk* disk)
+{
+        PED_ASSERT(disk != NULL);
+        PED_ASSERT(disk->dev != NULL);
+        int lpn;
+
+        /* lpn = largest partition number. */
+        if (ped_disk_get_max_supported_partition_count(disk, &lpn))
+                lpn = PED_MIN(lpn, _device_get_partition_range(disk->dev));
+        else
+                lpn = _device_get_partition_range(disk->dev);
+
+        /* Its not possible to support largest_partnum < 0.
+         * largest_partnum == 0 would mean does not support partitions.
+         * */
+        if (lpn < 0)
+                return 0;
+        int ret = 0;
+        int *ok = calloc (lpn, sizeof *ok);
+        if (!ok)
+                return 0;
+        int *errnums = ped_malloc(sizeof(int) * lpn);
+        if (!errnums)
+                goto cleanup;
+
+        /* Attempt to remove each and every partition, retrying for
+           up to max_sleep_seconds upon any failure due to EBUSY. */
+        unsigned int sleep_microseconds = 10000;
+        unsigned int max_sleep_seconds = 1;
+        unsigned int n_sleep = (max_sleep_seconds
+                                * 1000000 / sleep_microseconds);
+        int i;
+        for (i = 0; i < n_sleep; i++) {
+           if (i)
+               usleep (sleep_microseconds);
+            bool busy = false;
+            int j;
+            for (j = 0; j < lpn; j++) {
+                if (!ok[j]) {
+                    ok[j] = _blkpg_remove_partition (disk, j + 1);
+                    errnums[j] = errno;
+                    if (!ok[j] && errnums[j] == EBUSY)
+                        busy = true;
+                }
+            }
+            if (!busy)
+                break;
+        }
+
+        for (i = 1; i <= lpn; i++) {
+                const PedPartition *part = ped_disk_get_partition (disk, i);
+                if (part) {
+                        if (!ok[i - 1] && errnums[i - 1] == EBUSY) {
+                                struct hd_geometry geom;
+                                unsigned long long length = 0;
+                                /* get start and length of existing partition */
+                                char *dev_name = _device_get_part_path (disk->dev, i);
+                                if (!dev_name)
+                                        goto cleanup;
+                                int fd = open (dev_name, O_RDONLY);
+                                if (fd == -1
+                                   || ioctl (fd, HDIO_GETGEO, &geom)
+                                   || ioctl (fd, BLKGETSIZE64, &length)) {
+                                        ped_exception_throw (
+                                                             PED_EXCEPTION_BUG,
+                                                             PED_EXCEPTION_CANCEL,
+                           _("Unable to determine the size and length of %s."),
+                                                             dev_name);
+                                        if (fd != -1)
+                                                close (fd);
+                                        free (dev_name);
+                                        goto cleanup;
+                                }
+                                free (dev_name);
+                                length /= disk->dev->sector_size;
+                                close (fd);
+                                if (geom.start == part->geom.start
+                                   && length == part->geom.length)
+                                        ok[i - 1] = 1;
+                                /* If the new partition is unchanged and the
+                                  existing one was not removed because it was
+                                  in use, then reset the error flag and do not
+                                  try to add it since it is already there.  */
+                                continue;
+                        }
+
+                        /* add the (possibly modified or new) partition */
+                        if (!_blkpg_add_partition (disk, part)) {
+                                ped_exception_throw (
+                                        PED_EXCEPTION_ERROR,
+                                        PED_EXCEPTION_RETRY_CANCEL,
+                                        _("Failed to add partition %d (%s)"),
+                                        i, strerror (errno));
+                                goto cleanup;
+                        }
+                }
+        }
+
+        char *bad_part_list = NULL;
+        /* now warn about any errors */
+        for (i = 1; i <= lpn; i++) {
+               if (ok[i - 1] || errnums[i - 1] == ENXIO)
+                       continue;
+               if (bad_part_list == NULL) {
+                         bad_part_list = malloc (lpn * 5);
+                         if (!bad_part_list)
+                                 goto cleanup;
+                         bad_part_list[0] = 0;
+               }
+               sprintf (bad_part_list + strlen (bad_part_list), "%d, ", i);
+       }
+        if (bad_part_list == NULL)
+               ret = 1;
+       else {
+                bad_part_list[strlen (bad_part_list) - 2] = 0;
+                if (ped_exception_throw (
+                        PED_EXCEPTION_ERROR,
+                        PED_EXCEPTION_IGNORE_CANCEL,
+                        _("Partition(s) %s on %s have been written, but we have "
+                         "been unable to inform the kernel of the change, "
+                         "probably because it/they are in use.  As a result, "
+                          "the old partition(s) will remain in use.  You "
+                          "should reboot now before making further changes."),
+                        bad_part_list, disk->dev->path) == PED_EXCEPTION_IGNORE)
+                        ret = 1;
+               free (bad_part_list);
+        }
+ cleanup:
+        free (errnums);
+        free (ok);
+        return ret;
+}
+
 #ifdef ENABLE_DEVICE_MAPPER
 static int
 _dm_remove_map_name(char *name)
@@ -2278,7 +2613,7 @@ _dm_remove_map_name(char *name)
         rc = dm_task_run(task);
         dm_task_update_nodes();
         dm_task_destroy(task);
-        if (rc < 0)
+        if (!rc)
                 return 1;
 
         return 0;
@@ -2298,12 +2633,8 @@ _dm_is_part (struct dm_info *this, char *name)
                 return 0;
 
         dm_task_set_name(task, name);
-        rc = dm_task_run(task);
-        if (rc < 0) {
-                rc = 0;
+        if (!dm_task_run(task))
                 goto err;
-        }
-        rc = 0;
 
         memset(info, '\0', sizeof *info);
         dm_task_get_info(task, info);
@@ -2314,7 +2645,6 @@ _dm_is_part (struct dm_info *this, char *name)
         if (!deps)
                 goto err;
 
-        rc = 0;
         for (i = 0; i < deps->count; i++) {
                 unsigned int ma = major(deps->device[i]),
                              mi = minor(deps->device[i]);
@@ -2331,25 +2661,22 @@ err:
 static int
 _dm_remove_parts (PedDevice* dev)
 {
-        struct stat             dev_stat;
         struct dm_task*         task = NULL;
         struct dm_info*         info = alloca(sizeof *info);
         struct dm_names*        names = NULL;
         unsigned int            next = 0;
         int                     rc;
-
-        if (!_device_stat (dev, &dev_stat))
-                goto err;
+        LinuxSpecific*          arch_specific = LINUX_SPECIFIC (dev);
 
         task = dm_task_create(DM_DEVICE_LIST);
         if (!task)
                 goto err;
 
-        dm_task_set_major (task, major (dev_stat.st_rdev));
-        dm_task_set_minor (task, minor (dev_stat.st_rdev));
+        if (!dm_task_set_major_minor (task, arch_specific->major,
+                                      arch_specific->minor, 0))
+                goto err;
 
-        rc = dm_task_run(task);
-        if (rc < 0)
+        if (!dm_task_run(task))
                 goto err;
 
         memset(info, '\0', sizeof *info);
@@ -2391,36 +2718,37 @@ err:
 static int
 _dm_add_partition (PedDisk* disk, PedPartition* part)
 {
-        struct stat     dev_stat;
-        struct dm_task* task = NULL;
-        int             rc;
         char*           vol_name = NULL;
-        char*           dev_name = NULL;
+        const char*     dev_name = NULL;
         char*           params = NULL;
+        LinuxSpecific*  arch_specific = LINUX_SPECIFIC (disk->dev);
 
         if (!_has_partitions(disk))
                 return 0;
 
-        dev_name = _device_get_part_path (disk->dev, part->num);
-        if (!dev_name)
-                return 0;
+        /* Get map name from devicemapper */
+        struct dm_task *task = dm_task_create (DM_DEVICE_INFO);
+        if (!task)
+                goto err;
 
-        vol_name = strrchr (dev_name, '/');
-        if (vol_name && *vol_name && *(++vol_name))
-                vol_name = strdup (vol_name);
-        else
-                vol_name = strdup (dev_name);
-        if (!vol_name)
-                return 0;
+        if (!dm_task_set_major_minor (task, arch_specific->major,
+                                      arch_specific->minor, 0))
+                goto err;
 
-        if (!_device_stat (disk->dev, &dev_stat))
+        if (!dm_task_run(task))
                 goto err;
 
-        if (asprintf (&params, "%d:%d %lld", major (dev_stat.st_rdev),
-                      minor (dev_stat.st_rdev), part->geom.start) == -1)
+        dev_name = dm_task_get_name (task);
+
+        if (asprintf (&vol_name, "%sp%d", dev_name, part->num) == -1)
                 goto err;
 
-        if (!params)
+        /* Caution: dm_task_destroy frees dev_name.  */
+        dm_task_destroy (task);
+        task = NULL;
+
+        if (asprintf (&params, "%d:%d %lld", arch_specific->major,
+                      arch_specific->minor, part->geom.start) == -1)
                 goto err;
 
         task = dm_task_create (DM_DEVICE_CREATE);
@@ -2430,8 +2758,7 @@ _dm_add_partition (PedDisk* disk, PedPartition* part)
         dm_task_set_name (task, vol_name);
         dm_task_add_target (task, 0, part->geom.length,
                 "linear", params);
-        rc = dm_task_run(task);
-        if (rc >= 0) {
+        if (dm_task_run (task)) {
                 //printf("0 %ld linear %s\n", part->geom.length, params);
                 dm_task_update_nodes();
                 dm_task_destroy(task);
@@ -2480,68 +2807,98 @@ _dm_reread_part_table (PedDisk* disk)
 #endif
 
 static int
-_kernel_reread_part_table (PedDevice* dev)
+_have_blkpg ()
 {
-        LinuxSpecific*  arch_specific = LINUX_SPECIFIC (dev);
-        int             retry_count = 5;
+        static int have_blkpg = -1;
+        int kver;
 
-        sync();
-        while (ioctl (arch_specific->fd, BLKRRPART)) {
-                retry_count--;
-                sync();
-                if (!retry_count) {
-                        ped_exception_throw (
-                                PED_EXCEPTION_WARNING,
-                                PED_EXCEPTION_IGNORE,
-                        _("WARNING: the kernel failed to re-read the partition "
-                          "table on %s (%s).  As a result, it may not "
-                          "reflect all of your changes until after reboot."),
-                                dev->path, strerror (errno));
-                        return 0;
-                }
-        }
+        if (have_blkpg != -1)
+                return have_blkpg;
 
-        return 1;
+        kver = _get_linux_version();
+        return have_blkpg = kver >= KERNEL_VERSION (2,4,0) ? 1 : 0;
 }
 
+/* Return nonzero upon success, 0 if something fails.  */
 static int
 linux_disk_commit (PedDisk* disk)
 {
-       if (!_has_partitions (disk))
-               return 1;
+        if (!_has_partitions (disk))
+                return 1;
 
 #ifdef ENABLE_DEVICE_MAPPER
         if (disk->dev->type == PED_DEVICE_DM)
                 return _dm_reread_part_table (disk);
 #endif
         if (disk->dev->type != PED_DEVICE_FILE) {
-                return _kernel_reread_part_table (disk->dev);
+
+               /* We now require BLKPG support.  If this assertion fails,
+                  please write to the mailing list describing your system.
+                  Assuming it's never triggered, ...
+                  FIXME: remove this assertion in 2012.  */
+               assert (_have_blkpg ());
+
+               if (!_disk_sync_part_table (disk))
+                       return 0;
         }
 
         return 1;
 }
 
 #if USE_BLKID
-PedAlignment*
+static PedAlignment*
 linux_get_minimum_alignment(const PedDevice *dev)
 {
         blkid_topology tp = LINUX_SPECIFIC(dev)->topology;
+        if (!tp)
+                return NULL;
 
-        if (!tp || blkid_topology_get_minimum_io_size(tp) == 0)
-                return NULL; /* ped_alignment_none */
+        if (blkid_topology_get_minimum_io_size(tp) == 0)
+                return ped_alignment_new(
+                        blkid_topology_get_alignment_offset(tp) /
+                                dev->sector_size,
+                        dev->phys_sector_size / dev->sector_size);
 
         return ped_alignment_new(
                 blkid_topology_get_alignment_offset(tp) / dev->sector_size,
                 blkid_topology_get_minimum_io_size(tp) / dev->sector_size);
 }
 
-PedAlignment*
+static PedAlignment*
 linux_get_optimum_alignment(const PedDevice *dev)
 {
         blkid_topology tp = LINUX_SPECIFIC(dev)->topology;
+        if (!tp)
+                return NULL;
+
+        /* When PED_DEFAULT_ALIGNMENT is divisible by the *_io_size or
+          there are no *_io_size values, use the PED_DEFAULT_ALIGNMENT
+           If one or the other will not divide evenly, fall through to
+           previous logic. */
+        unsigned long optimal_io = blkid_topology_get_optimal_io_size(tp);
+        unsigned long minimum_io = blkid_topology_get_minimum_io_size(tp);
+        if (
+            (!optimal_io && !minimum_io)
+           || (optimal_io && PED_DEFAULT_ALIGNMENT % optimal_io == 0
+               && minimum_io && PED_DEFAULT_ALIGNMENT % minimum_io == 0)
+           || (!minimum_io && optimal_io
+               && PED_DEFAULT_ALIGNMENT % optimal_io == 0)
+           || (!optimal_io && minimum_io
+               && PED_DEFAULT_ALIGNMENT % minimum_io == 0)
+           ) {
+            /* DASD needs to use minimum alignment */
+            if (dev->type == PED_DEVICE_DASD)
+                return linux_get_minimum_alignment(dev);
+
+            return ped_alignment_new(
+                    blkid_topology_get_alignment_offset(tp) / dev->sector_size,
+                    PED_DEFAULT_ALIGNMENT / dev->sector_size);
+        }
 
-        if (!tp || blkid_topology_get_optimal_io_size(tp) == 0)
-                return NULL; /* ped_alignment_none */
+        /* If optimal_io_size is 0 and we don't meet the other criteria
+           for using the device.c default, return the minimum alignment. */
+        if (blkid_topology_get_optimal_io_size(tp) == 0)
+                return linux_get_minimum_alignment(dev);
 
         return ped_alignment_new(
                 blkid_topology_get_alignment_offset(tp) / dev->sector_size,