Blame SOURCES/0046-libparted-Backport-partition-resize-code.patch

2a8729
From 31ab97cfe0233191a73a1dd9cb7cd193451491da Mon Sep 17 00:00:00 2001
2a8729
From: "Brian C. Lane" <bcl@redhat.com>
2a8729
Date: Fri, 11 Aug 2017 08:37:11 -0700
2a8729
Subject: [PATCH 46/48] libparted: Backport partition resize code
2a8729
2a8729
This adds _blkpg_resize_partition, _dm_resize_partition, and adjust the
2a8729
current code to support it. Changes are somewhat extensive, since they
2a8729
also touch _disk_sync_part_table.
2a8729
2a8729
This is based on code from commit f09ca967a0bc443b869a6fad5b5ffe8e95c3fe9a
2a8729
2a8729
Related: rhbz#1423357
2a8729
---
2a8729
 libparted/arch/linux.c | 673 +++++++++++++++++++++++++++++--------------------
2a8729
 1 file changed, 403 insertions(+), 270 deletions(-)
2a8729
2a8729
diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c
2a8729
index fa329f4..6e78faf 100644
2a8729
--- a/libparted/arch/linux.c
2a8729
+++ b/libparted/arch/linux.c
2a8729
@@ -288,6 +288,7 @@ struct blkdev_ioctl_param {
2a8729
 
2a8729
 static char* _device_get_part_path (PedDevice* dev, int num);
2a8729
 static int _partition_is_mounted_by_path (const char* path);
2a8729
+static unsigned int _device_get_partition_range(PedDevice const* dev);
2a8729
 static int _device_open (PedDevice* dev, int flags);
2a8729
 static int _device_open_ro (PedDevice* dev);
2a8729
 static int _device_close (PedDevice* dev);
2a8729
@@ -446,6 +447,17 @@ _is_blkext_major (int major)
2a8729
 
2a8729
 #ifdef ENABLE_DEVICE_MAPPER
2a8729
 static int
2a8729
+_dm_task_run_wait (struct dm_task *task, uint32_t cookie)
2a8729
+{
2a8729
+        int rc = 0;
2a8729
+
2a8729
+        rc = dm_task_run (task);
2a8729
+        dm_udev_wait (cookie);
2a8729
+
2a8729
+        return rc;
2a8729
+}
2a8729
+
2a8729
+static int
2a8729
 _is_dm_major (int major)
2a8729
 {
2a8729
         return _major_type_in_devices (major, "device-mapper");
2a8729
@@ -1521,6 +1533,7 @@ _flush_cache (PedDevice* dev)
2a8729
 {
2a8729
         LinuxSpecific*  arch_specific = LINUX_SPECIFIC (dev);
2a8729
         int             i;
2a8729
+        int             lpn = _device_get_partition_range(dev);
2a8729
 
2a8729
         if (dev->read_only)
2a8729
                 return;
2a8729
@@ -1532,7 +1545,7 @@ _flush_cache (PedDevice* dev)
2a8729
         if (_have_kern26())
2a8729
                 return;
2a8729
 
2a8729
-        for (i = 1; i < 16; i++) {
2a8729
+        for (i = 1; i < lpn; i++) {
2a8729
                 char*           name;
2a8729
                 int             fd;
2a8729
 
2a8729
@@ -2268,28 +2281,63 @@ zasprintf (const char *format, ...)
2a8729
   return r < 0 ? NULL : resultp;
2a8729
 }
2a8729
 
2a8729
+#ifdef ENABLE_DEVICE_MAPPER
2a8729
+static char *
2a8729
+dm_canonical_path (PedDevice const *dev)
2a8729
+{
2a8729
+        LinuxSpecific const *arch_specific = LINUX_SPECIFIC (dev);
2a8729
+
2a8729
+        /* Get map name from devicemapper */
2a8729
+        struct dm_task *task = dm_task_create (DM_DEVICE_INFO);
2a8729
+        if (!task)
2a8729
+                goto err;
2a8729
+        if (!dm_task_set_major_minor (task, arch_specific->major,
2a8729
+                                      arch_specific->minor, 0))
2a8729
+                goto err;
2a8729
+        if (!dm_task_run(task))
2a8729
+                goto err;
2a8729
+        char *dev_name = zasprintf ("/dev/mapper/%s", dm_task_get_name (task));
2a8729
+        if (dev_name == NULL)
2a8729
+                goto err;
2a8729
+        dm_task_destroy (task);
2a8729
+        return dev_name;
2a8729
+err:
2a8729
+        return NULL;
2a8729
+}
2a8729
+#endif
2a8729
+
2a8729
 static char*
2a8729
 _device_get_part_path (PedDevice *dev, int num)
2a8729
 {
2a8729
-        size_t path_len = strlen (dev->path);
2a8729
-
2a8729
+        char *devpath;
2a8729
+        size_t path_len;
2a8729
         char *result;
2a8729
+#ifdef ENABLE_DEVICE_MAPPER
2a8729
+        devpath = (dev->type == PED_DEVICE_DM
2a8729
+                         ? dm_canonical_path (dev) : dev->path);
2a8729
+#else
2a8729
+        devpath = dev->path;
2a8729
+#endif
2a8729
+        path_len = strlen (devpath);
2a8729
         /* Check for devfs-style /disc => /partN transformation
2a8729
            unconditionally; the system might be using udev with devfs rules,
2a8729
            and if not the test is harmless. */
2a8729
-        if (5 < path_len && !strcmp (dev->path + path_len - 5, "/disc")) {
2a8729
+        if (5 < path_len && !strcmp (devpath + path_len - 5, "/disc")) {
2a8729
                 /* replace /disc with /part%d */
2a8729
                 result = zasprintf ("%.*s/part%d",
2a8729
-                                    (int) (path_len - 5), dev->path, num);
2a8729
+                                    (int) (path_len - 5), devpath, num);
2a8729
         } else {
2a8729
                 char const *p = (dev->type == PED_DEVICE_DAC960
2a8729
                                  || dev->type == PED_DEVICE_CPQARRAY
2a8729
                                  || dev->type == PED_DEVICE_ATARAID
2a8729
                                  || isdigit (dev->path[path_len - 1])
2a8729
                                  ? "p" : "");
2a8729
-                result = zasprintf ("%s%s%d", dev->path, p, num);
2a8729
+                result = zasprintf ("%s%s%d", devpath, p, num);
2a8729
         }
2a8729
-
2a8729
+#ifdef ENABLE_DEVICE_MAPPER
2a8729
+        if (dev->type == PED_DEVICE_DM)
2a8729
+                free (devpath);
2a8729
+#endif
2a8729
         return result;
2a8729
 }
2a8729
 
2a8729
@@ -2438,6 +2486,62 @@ _blkpg_add_partition (PedDisk* disk, const PedPartition *part)
2a8729
 
2a8729
         if (!_blkpg_part_command (disk->dev, &linux_part,
2a8729
                                   BLKPG_ADD_PARTITION)) {
2a8729
+            return 0;
2a8729
+        }
2a8729
+
2a8729
+        return 1;
2a8729
+}
2a8729
+
2a8729
+static int
2a8729
+_blkpg_remove_partition (PedDisk* disk, int n)
2a8729
+{
2a8729
+        struct blkpg_partition  linux_part;
2a8729
+
2a8729
+        memset (&linux_part, 0, sizeof (linux_part));
2a8729
+        linux_part.pno = n;
2a8729
+        return _blkpg_part_command (disk->dev, &linux_part,
2a8729
+                                    BLKPG_DEL_PARTITION);
2a8729
+}
2a8729
+
2a8729
+#ifdef BLKPG_RESIZE_PARTITION
2a8729
+static int _blkpg_resize_partition (PedDisk* disk, const PedPartition *part)
2a8729
+{
2a8729
+        struct blkpg_partition  linux_part;
2a8729
+        char*                   dev_name;
2a8729
+
2a8729
+        PED_ASSERT(disk != NULL);
2a8729
+        PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0);
2a8729
+
2a8729
+        dev_name = _device_get_part_path (disk->dev, part->num);
2a8729
+        if (!dev_name)
2a8729
+                return 0;
2a8729
+        memset (&linux_part, 0, sizeof (linux_part));
2a8729
+        linux_part.start = part->geom.start * disk->dev->sector_size;
2a8729
+        /* see fs/partitions/msdos.c:msdos_partition(): "leave room for LILO" */
2a8729
+        if (part->type & PED_PARTITION_EXTENDED) {
2a8729
+                if (disk->dev->sector_size == 512) {
2a8729
+                        linux_part.length = 2;
2a8729
+                        PedPartition *walk;
2a8729
+                        /* if the second sector is claimed by a logical partition,
2a8729
+                           then there's just no room for lilo, so don't try to use it */
2a8729
+                        for (walk = part->part_list; walk; walk = walk->next) {
2a8729
+                                if (walk->geom.start == part->geom.start+1)
2a8729
+                                        linux_part.length = 1;
2a8729
+                        }
2a8729
+                } else {
2a8729
+                        linux_part.length = 1;
2a8729
+                }
2a8729
+                linux_part.length *= disk->dev->sector_size;
2a8729
+        }
2a8729
+        else
2a8729
+                linux_part.length = part->geom.length * disk->dev->sector_size;
2a8729
+        linux_part.pno = part->num;
2a8729
+        strncpy (linux_part.devname, dev_name, BLKPG_DEVNAMELTH);
2a8729
+
2a8729
+        free (dev_name);
2a8729
+
2a8729
+        if (!_blkpg_part_command (disk->dev, &linux_part,
2a8729
+                                  BLKPG_RESIZE_PARTITION)) {
2a8729
                 return ped_exception_throw (
2a8729
                         PED_EXCEPTION_ERROR,
2a8729
                         PED_EXCEPTION_IGNORE_CANCEL,
2a8729
@@ -2454,17 +2558,7 @@ _blkpg_add_partition (PedDisk* disk, const PedPartition *part)
2a8729
 
2a8729
         return 1;
2a8729
 }
2a8729
-
2a8729
-static int
2a8729
-_blkpg_remove_partition (PedDisk* disk, int n)
2a8729
-{
2a8729
-        struct blkpg_partition  linux_part;
2a8729
-
2a8729
-        memset (&linux_part, 0, sizeof (linux_part));
2a8729
-        linux_part.pno = n;
2a8729
-        return _blkpg_part_command (disk->dev, &linux_part,
2a8729
-                                    BLKPG_DEL_PARTITION);
2a8729
-}
2a8729
+#endif
2a8729
 
2a8729
 /* Read the integer from /sys/block/DEV_BASE/ENTRY and set *VAL
2a8729
    to that value, where DEV_BASE is the last component of DEV->path.
2a8729
@@ -2586,6 +2680,8 @@ static unsigned int
2a8729
 _device_get_partition_range(PedDevice const* dev)
2a8729
 {
2a8729
         int range;
2a8729
+        if (dev->type == PED_DEVICE_DM)
2a8729
+                return MAX_NUM_PARTS;
2a8729
         bool ok = _sysfs_int_entry_from_dev(dev, "ext_range", &range);
2a8729
 
2a8729
         if (!ok)
2a8729
@@ -2594,264 +2690,78 @@ _device_get_partition_range(PedDevice const* dev)
2a8729
         return range > 1 ? range : 0;
2a8729
 }
2a8729
 
2a8729
-/*
2a8729
- * Sync the partition table in two step process:
2a8729
- * 1. Remove all of the partitions from the kernel's tables, but do not attempt
2a8729
- *    removal of any partition for which the corresponding ioctl call fails.
2a8729
- * 2. Add all the partitions that we hold in disk, throwing a warning
2a8729
- *    if we cannot because step 1 failed to remove it and it is not being
2a8729
- *    added back with the same start and length.
2a8729
- *
2a8729
- * To achieve this two step process we must calculate the minimum number of
2a8729
- * maximum possible partitions between what linux supports and what the label
2a8729
- * type supports. EX:
2a8729
- *
2a8729
- * number=MIN(max_parts_supported_in_linux,max_parts_supported_in_msdos_tables)
2a8729
- */
2a8729
-static int
2a8729
-_disk_sync_part_table (PedDisk* disk)
2a8729
-{
2a8729
-        PED_ASSERT(disk != NULL);
2a8729
-        PED_ASSERT(disk->dev != NULL);
2a8729
-        int lpn;
2a8729
-
2a8729
-        unsigned int part_range = _device_get_partition_range(disk->dev);
2a8729
-
2a8729
-        /* lpn = largest partition number. */
2a8729
-        if (ped_disk_get_max_supported_partition_count(disk, &lpn))
2a8729
-                lpn = PED_MIN(lpn, part_range);
2a8729
-        else
2a8729
-                lpn = part_range;
2a8729
-
2a8729
-        /* Its not possible to support largest_partnum < 0.
2a8729
-         * largest_partnum == 0 would mean does not support partitions.
2a8729
-         * */
2a8729
-        if (lpn < 1)
2a8729
-                return 0;
2a8729
-        int ret = 0;
2a8729
-        int *ok = calloc (lpn, sizeof *ok);
2a8729
-        if (!ok)
2a8729
-                return 0;
2a8729
-        int *errnums = ped_malloc(sizeof(int) * lpn);
2a8729
-        if (!errnums)
2a8729
-                goto cleanup;
2a8729
-
2a8729
-        /* Attempt to remove each and every partition, retrying for
2a8729
-           up to max_sleep_seconds upon any failure due to EBUSY. */
2a8729
-        unsigned int sleep_microseconds = 10000;
2a8729
-        unsigned int max_sleep_seconds = 1;
2a8729
-        unsigned int n_sleep = (max_sleep_seconds
2a8729
-                                * 1000000 / sleep_microseconds);
2a8729
-        int i;
2a8729
-        for (i = 0; i < n_sleep; i++) {
2a8729
-	    if (i)
2a8729
-		usleep (sleep_microseconds);
2a8729
-            bool busy = false;
2a8729
-            int j;
2a8729
-            for (j = 0; j < lpn; j++) {
2a8729
-                if (!ok[j]) {
2a8729
-                    ok[j] = _blkpg_remove_partition (disk, j + 1);
2a8729
-                    errnums[j] = errno;
2a8729
-                    if (!ok[j] && errnums[j] == EBUSY)
2a8729
-                        busy = true;
2a8729
-                }
2a8729
-            }
2a8729
-            if (!busy)
2a8729
-                break;
2a8729
-        }
2a8729
-
2a8729
-        for (i = 1; i <= lpn; i++) {
2a8729
-                PedPartition *part = ped_disk_get_partition (disk, i);
2a8729
-                if (part) {
2a8729
-                        if (!ok[i - 1] && errnums[i - 1] == EBUSY) {
2a8729
-                                unsigned long long length;
2a8729
-                                unsigned long long start;
2a8729
-                                /* get start and length of existing partition */
2a8729
-                                if (!_kernel_get_partition_start_and_length(part,
2a8729
-                                                                &start, &length))
2a8729
-                                        goto cleanup;
2a8729
-                                if (start == part->geom.start
2a8729
-				    && length == part->geom.length)
2a8729
-                                        ok[i - 1] = 1;
2a8729
-                                /* If the new partition is unchanged and the
2a8729
-				   existing one was not removed because it was
2a8729
-				   in use, then reset the error flag and do not
2a8729
-				   try to add it since it is already there.  */
2a8729
-                                continue;
2a8729
-                        }
2a8729
-
2a8729
-                        /* add the (possibly modified or new) partition */
2a8729
-                        if (!_blkpg_add_partition (disk, part)) {
2a8729
-                                ped_exception_throw (
2a8729
-                                        PED_EXCEPTION_ERROR,
2a8729
-                                        PED_EXCEPTION_RETRY_CANCEL,
2a8729
-                                        _("Failed to add partition %d (%s)"),
2a8729
-                                        i, strerror (errno));
2a8729
-                                goto cleanup;
2a8729
-                        }
2a8729
-                }
2a8729
-        }
2a8729
-
2a8729
-        char *bad_part_list = NULL;
2a8729
-        /* now warn about any errors */
2a8729
-        for (i = 1; i <= lpn; i++) {
2a8729
-		if (ok[i - 1] || errnums[i - 1] == ENXIO)
2a8729
-			continue;
2a8729
-		if (bad_part_list == NULL) {
2a8729
-			  bad_part_list = malloc (lpn * 5);
2a8729
-			  if (!bad_part_list)
2a8729
-				  goto cleanup;
2a8729
-			  bad_part_list[0] = 0;
2a8729
-		}
2a8729
-		sprintf (bad_part_list + strlen (bad_part_list), "%d, ", i);
2a8729
-	}
2a8729
-        if (bad_part_list == NULL)
2a8729
-		ret = 1;
2a8729
-	else {
2a8729
-                bad_part_list[strlen (bad_part_list) - 2] = 0;
2a8729
-                if (ped_exception_throw (
2a8729
-                        PED_EXCEPTION_ERROR,
2a8729
-                        PED_EXCEPTION_IGNORE_CANCEL,
2a8729
-                        _("Partition(s) %s on %s have been written, but we have "
2a8729
-			  "been unable to inform the kernel of the change, "
2a8729
-			  "probably because it/they are in use.  As a result, "
2a8729
-                          "the old partition(s) will remain in use.  You "
2a8729
-                          "should reboot now before making further changes."),
2a8729
-                        bad_part_list, disk->dev->path) == PED_EXCEPTION_IGNORE)
2a8729
-                        ret = 1;
2a8729
-		free (bad_part_list);
2a8729
-        }
2a8729
- cleanup:
2a8729
-        free (errnums);
2a8729
-        free (ok);
2a8729
-        return ret;
2a8729
-}
2a8729
-
2a8729
 #ifdef ENABLE_DEVICE_MAPPER
2a8729
 static int
2a8729
-_dm_remove_map_name(char *name)
2a8729
+_dm_remove_partition(PedDisk* disk, int partno)
2a8729
 {
2a8729
-        struct dm_task  *task = NULL;
2a8729
         int             rc = 0;
2a8729
         uint32_t        cookie = 0;
2a8729
+        char            *part_name = _device_get_part_path (disk->dev, partno);
2a8729
 
2a8729
-        task = dm_task_create(DM_DEVICE_REMOVE);
2a8729
+        int fd = open (part_name, O_RDONLY | O_EXCL);
2a8729
+        if (fd == -1) {
2a8729
+                if (errno == ENOENT)
2a8729
+                        errno = ENXIO; /* nothing to remove, device already doesn't exist */
2a8729
+                goto err;
2a8729
+        }
2a8729
+        close (fd);
2a8729
+        struct dm_task *task = dm_task_create(DM_DEVICE_REMOVE);
2a8729
         if (!task)
2a8729
-                return 1;
2a8729
-
2a8729
-        dm_task_set_name (task, name);
2a8729
-        if (!dm_task_set_cookie(task, &cookie, 0))
2a8729
-            goto err;
2a8729
-
2a8729
-        rc = dm_task_run(task);
2a8729
-        dm_udev_wait(cookie);
2a8729
+                goto err;
2a8729
+        dm_task_set_name (task, part_name);
2a8729
+        if (!dm_task_set_cookie (task, &cookie, 0))
2a8729
+                goto err;
2a8729
+        rc = _dm_task_run_wait (task, cookie);
2a8729
         dm_task_update_nodes();
2a8729
-err:
2a8729
         dm_task_destroy(task);
2a8729
-        if (!rc)
2a8729
-                return 1;
2a8729
-
2a8729
-        return 0;
2a8729
+err:
2a8729
+        free (part_name);
2a8729
+        return rc;
2a8729
 }
2a8729
 
2a8729
-static int
2a8729
-_dm_is_part (struct dm_info *this, char *name)
2a8729
+static bool
2a8729
+_dm_get_partition_start_and_length(PedPartition const *part,
2a8729
+                                   unsigned long long *start,
2a8729
+                                   unsigned long long *length)
2a8729
 {
2a8729
         struct dm_task* task = NULL;
2a8729
-        struct dm_info* info = alloca(sizeof *info);
2a8729
-        struct dm_deps* deps = NULL;
2a8729
         int             rc = 0;
2a8729
-        unsigned int    i;
2a8729
 
2a8729
-        task = dm_task_create(DM_DEVICE_DEPS);
2a8729
-        if (!task)
2a8729
+        if (!(task = dm_task_create(DM_DEVICE_TABLE)))
2a8729
                 return 0;
2a8729
-
2a8729
-        dm_task_set_name(task, name);
2a8729
-        if (!dm_task_run(task))
2a8729
+        char *path = _device_get_part_path (part->disk->dev, part->num);
2a8729
+        PED_ASSERT(path);
2a8729
+        /* libdevmapper likes to complain on stderr instead of quietly
2a8729
+           returning ENOENT or ENXIO, so try to stat first */
2a8729
+        struct stat st;
2a8729
+        if (stat(path, &st))
2a8729
                 goto err;
2a8729
-
2a8729
-        memset(info, '\0', sizeof *info);
2a8729
-        dm_task_get_info(task, info);
2a8729
-        if (!info->exists)
2a8729
+        dm_task_set_name(task, path);
2a8729
+        if (!dm_task_run(task))
2a8729
                 goto err;
2a8729
 
2a8729
-        deps = dm_task_get_deps(task);
2a8729
-        if (!deps)
2a8729
+        int major, minor;
2a8729
+        char *params;
2a8729
+        char *target_type;
2a8729
+        dm_get_next_target(task, NULL, (uint64_t *)start, (uint64_t *)length, &target_type, &params);
2a8729
+        if (sscanf (params, "%d:%d %Ld", &major, &minor, start) != 3)
2a8729
                 goto err;
2a8729
+        rc = 1;
2a8729
 
2a8729
-        for (i = 0; i < deps->count; i++) {
2a8729
-                unsigned int ma = major(deps->device[i]),
2a8729
-                             mi = minor(deps->device[i]);
2a8729
-
2a8729
-                if (ma == this->major && mi == this->minor)
2a8729
-                        rc = 1;
2a8729
-        }
2a8729
-
2a8729
+        /* device-mapper uses 512b units, make sure we return length and start in terms of the device's
2a8729
+         * sector size.
2a8729
+         */
2a8729
+        *start /= (part->disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT);
2a8729
+        *length /= (part->disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT);
2a8729
 err:
2a8729
+        free (path);
2a8729
         dm_task_destroy(task);
2a8729
         return rc;
2a8729
 }
2a8729
 
2a8729
-static int
2a8729
-_dm_remove_parts (PedDevice* dev)
2a8729
-{
2a8729
-        struct dm_task*         task = NULL;
2a8729
-        struct dm_info*         info = alloca(sizeof *info);
2a8729
-        struct dm_names*        names = NULL;
2a8729
-        unsigned int            next = 0;
2a8729
-        int                     rc;
2a8729
-        LinuxSpecific*          arch_specific = LINUX_SPECIFIC (dev);
2a8729
-
2a8729
-        task = dm_task_create(DM_DEVICE_LIST);
2a8729
-        if (!task)
2a8729
-                goto err;
2a8729
-
2a8729
-        if (!dm_task_set_major_minor (task, arch_specific->major,
2a8729
-                                      arch_specific->minor, 0))
2a8729
-                goto err;
2a8729
-
2a8729
-        if (!dm_task_run(task))
2a8729
-                goto err;
2a8729
-
2a8729
-        memset(info, '\0', sizeof *info);
2a8729
-        dm_task_get_info(task, info);
2a8729
-        if (!info->exists)
2a8729
-                goto err;
2a8729
-
2a8729
-        names = dm_task_get_names(task);
2a8729
-        if (!names)
2a8729
-                goto err;
2a8729
-
2a8729
-        rc = 0;
2a8729
-        do {
2a8729
-                names = (void *) ((char *) names + next);
2a8729
-
2a8729
-                if (_dm_is_part(info, names->name))
2a8729
-                        rc += _dm_remove_map_name(names->name);
2a8729
-
2a8729
-                next = names->next;
2a8729
-        } while (next);
2a8729
-
2a8729
-        dm_task_update_nodes();
2a8729
-        dm_task_destroy(task);
2a8729
-        task = NULL;
2a8729
-
2a8729
-        if (!rc)
2a8729
-                return 1;
2a8729
-err:
2a8729
-        if (task)
2a8729
-                dm_task_destroy(task);
2a8729
-        ped_exception_throw (PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE,
2a8729
-                _("parted was unable to re-read the partition "
2a8729
-                  "table on %s (%s).  This means Linux won't know "
2a8729
-                  "anything about the modifications you made. "),
2a8729
-                dev->path, strerror (errno));
2a8729
-        return 0;
2a8729
-}
2a8729
 
2a8729
 static int
2a8729
-_dm_add_partition (PedDisk* disk, PedPartition* part)
2a8729
+_dm_add_partition (PedDisk* disk, const PedPartition* part)
2a8729
 {
2a8729
         char*           vol_name = NULL;
2a8729
         const char*     dev_name = NULL;
2a8729
@@ -2908,9 +2818,8 @@ _dm_add_partition (PedDisk* disk, PedPartition* part)
2a8729
                 "linear", params);
2a8729
         if (!dm_task_set_cookie(task, &cookie, 0))
2a8729
             goto err;
2a8729
-        if (dm_task_run (task)) {
2a8729
+        if (_dm_task_run_wait (task, cookie)) {
2a8729
                 //printf("0 %ld linear %s\n", part->geom.length, params);
2a8729
-                dm_udev_wait(cookie);
2a8729
                 dm_task_update_nodes();
2a8729
                 dm_task_destroy(task);
2a8729
                 free(params);
2a8729
@@ -2918,8 +2827,7 @@ _dm_add_partition (PedDisk* disk, PedPartition* part)
2a8729
                 free(vol_name);
2a8729
                 return 1;
2a8729
         } else {
2a8729
-                dm_udev_wait(cookie);
2a8729
-                _dm_remove_map_name(vol_name);
2a8729
+                _dm_remove_partition (disk, part->num);
2a8729
         }
2a8729
 err:
2a8729
         dm_task_update_nodes();
2a8729
@@ -2932,30 +2840,259 @@ err:
2a8729
 }
2a8729
 
2a8729
 static int
2a8729
-_dm_reread_part_table (PedDisk* disk)
2a8729
+_dm_resize_partition (PedDisk* disk, const PedPartition* part)
2a8729
 {
2a8729
-        int largest_partnum = ped_disk_get_last_partition_num (disk);
2a8729
-        int     rc = 1;
2a8729
-        int     i;
2a8729
+        LinuxSpecific*  arch_specific = LINUX_SPECIFIC (disk->dev);
2a8729
+        char*           params = NULL;
2a8729
+        char*           vol_name = NULL;
2a8729
+        const char*     dev_name = NULL;
2a8729
+        uint32_t        cookie = 0;
2a8729
+        int             rc = 0;
2a8729
 
2a8729
-        sync();
2a8729
-        if (!_dm_remove_parts(disk->dev))
2a8729
-                rc = 0;
2a8729
+        /* Get map name from devicemapper */
2a8729
+        struct dm_task *task = dm_task_create (DM_DEVICE_INFO);
2a8729
+        if (!task)
2a8729
+                goto err;
2a8729
 
2a8729
-        for (i = 1; i <= largest_partnum; i++) {
2a8729
-                PedPartition*      part;
2a8729
+        if (!dm_task_set_major_minor (task, arch_specific->major,
2a8729
+                                      arch_specific->minor, 0))
2a8729
+                goto err;
2a8729
 
2a8729
-                part = ped_disk_get_partition (disk, i);
2a8729
-                if (!part)
2a8729
-                        continue;
2a8729
+        if (!dm_task_run(task))
2a8729
+                goto err;
2a8729
+
2a8729
+        dev_name = dm_task_get_name (task);
2a8729
+        size_t name_len = strlen (dev_name);
2a8729
+        vol_name = zasprintf ("%s%s%d",
2a8729
+                              dev_name,
2a8729
+                              isdigit (dev_name[name_len - 1]) ? "p" : "",
2a8729
+                              part->num);
2a8729
+        if (vol_name == NULL)
2a8729
+                goto err;
2a8729
+
2a8729
+        /* Caution: dm_task_destroy frees dev_name.  */
2a8729
+        dm_task_destroy (task);
2a8729
+        task = NULL;
2a8729
+
2a8729
+        /* device-mapper uses 512b units, not the device's sector size */
2a8729
+        if ( ! (params = zasprintf ("%d:%d %lld", arch_specific->major,
2a8729
+                                    arch_specific->minor,
2a8729
+                                    part->geom.start * (disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT))))
2a8729
+                goto err;
2a8729
+
2a8729
+        task = dm_task_create (DM_DEVICE_RELOAD);
2a8729
+        if (!task)
2a8729
+                goto err;
2a8729
 
2a8729
-                if (!_dm_add_partition (disk, part))
2a8729
-                        rc = 0;
2a8729
+        dm_task_set_name (task, vol_name);
2a8729
+        /* device-mapper uses 512b units, not the device's sector size */
2a8729
+        dm_task_add_target (task, 0, part->geom.length * (disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT),
2a8729
+                "linear", params);
2a8729
+        /* NOTE: DM_DEVICE_RELOAD doesn't generate udev events, so no cookie is needed (it will freeze).
2a8729
+         *       DM_DEVICE_RESUME does, so get a cookie and synchronize with udev.
2a8729
+         */
2a8729
+        if (dm_task_run (task)) {
2a8729
+                dm_task_destroy (task);
2a8729
+                task = dm_task_create (DM_DEVICE_RESUME);
2a8729
+                if (!task)
2a8729
+                        goto err;
2a8729
+                dm_task_set_name (task, vol_name);
2a8729
+                if (!dm_task_set_cookie (task, &cookie, 0))
2a8729
+                        goto err;
2a8729
+                if (_dm_task_run_wait (task, cookie)) {
2a8729
+                        rc = 1;
2a8729
+                }
2a8729
         }
2a8729
+err:
2a8729
+        dm_task_update_nodes();
2a8729
+        if (task)
2a8729
+                dm_task_destroy (task);
2a8729
+        free (params);
2a8729
+        free (vol_name);
2a8729
         return rc;
2a8729
 }
2a8729
+
2a8729
 #endif
2a8729
 
2a8729
+/*
2a8729
+ * Sync the partition table in two step process:
2a8729
+ * 1. Remove all of the partitions from the kernel's tables, but do not attempt
2a8729
+ *    removal of any partition for which the corresponding ioctl call fails.
2a8729
+ * 2. Add all the partitions that we hold in disk, throwing a warning
2a8729
+ *    if we cannot because step 1 failed to remove it and it is not being
2a8729
+ *    added back with the same start and length.
2a8729
+ *
2a8729
+ * To achieve this two step process we must calculate the minimum number of
2a8729
+ * maximum possible partitions between what linux supports and what the label
2a8729
+ * type supports. EX:
2a8729
+ *
2a8729
+ * number=MIN(max_parts_supported_in_linux,max_parts_supported_in_msdos_tables)
2a8729
+ */
2a8729
+static int
2a8729
+_disk_sync_part_table (PedDisk* disk)
2a8729
+{
2a8729
+        PED_ASSERT(disk != NULL);
2a8729
+        PED_ASSERT(disk->dev != NULL);
2a8729
+        int lpn, lpn2;
2a8729
+        unsigned int part_range = _device_get_partition_range(disk->dev);
2a8729
+        int (*add_partition)(PedDisk* disk, const PedPartition *part);
2a8729
+        int (*resize_partition)(PedDisk* disk, const PedPartition *part);
2a8729
+        int (*remove_partition)(PedDisk* disk, int partno);
2a8729
+        bool (*get_partition_start_and_length)(PedPartition const *part,
2a8729
+                                               unsigned long long *start,
2a8729
+                                               unsigned long long *length);
2a8729
+
2a8729
+
2a8729
+#ifdef ENABLE_DEVICE_MAPPER
2a8729
+        if (disk->dev->type == PED_DEVICE_DM) {
2a8729
+                add_partition = _dm_add_partition;
2a8729
+                remove_partition = _dm_remove_partition;
2a8729
+                resize_partition = _dm_resize_partition;
2a8729
+                get_partition_start_and_length = _dm_get_partition_start_and_length;
2a8729
+        } else
2a8729
+#endif
2a8729
+        {
2a8729
+                add_partition = _blkpg_add_partition;
2a8729
+                remove_partition = _blkpg_remove_partition;
2a8729
+#ifdef BLKPG_RESIZE_PARTITION
2a8729
+                resize_partition = _blkpg_resize_partition;
2a8729
+#else
2a8729
+                resize_partition = NULL;
2a8729
+#endif
2a8729
+                get_partition_start_and_length = _kernel_get_partition_start_and_length;
2a8729
+        }
2a8729
+
2a8729
+        /* lpn = largest partition number.
2a8729
+         * for remove pass, use greater of device or label limit */
2a8729
+        if (ped_disk_get_max_supported_partition_count(disk, &lpn))
2a8729
+                lpn = PED_MAX(lpn, part_range);
2a8729
+        else
2a8729
+                lpn = part_range;
2a8729
+        /* for add pass, use lesser of device or label limit */
2a8729
+        if (ped_disk_get_max_supported_partition_count(disk, &lpn2))
2a8729
+                lpn2 = PED_MIN(lpn2, part_range);
2a8729
+        else
2a8729
+                lpn2 = part_range;
2a8729
+        /* Its not possible to support largest_partnum < 0.
2a8729
+         * largest_partnum == 0 would mean does not support partitions.
2a8729
+         * */
2a8729
+        if (lpn < 1)
2a8729
+                return 0;
2a8729
+        int ret = 0;
2a8729
+        int *ok = calloc (lpn, sizeof *ok);
2a8729
+        if (!ok)
2a8729
+                return 0;
2a8729
+        int *errnums = ped_malloc(sizeof(int) * lpn);
2a8729
+        if (!errnums)
2a8729
+                goto cleanup;
2a8729
+
2a8729
+        int i;
2a8729
+        /* remove old partitions first */
2a8729
+        for (i = 1; i <= lpn; i++) {
2a8729
+                PedPartition *part = ped_disk_get_partition (disk, i);
2a8729
+                if (part) {
2a8729
+                        unsigned long long length;
2a8729
+                        unsigned long long start;
2a8729
+                        /* get start and length of existing partition */
2a8729
+                        if (get_partition_start_and_length(part,
2a8729
+                                                           &start, &length)
2a8729
+                            && start == part->geom.start
2a8729
+                            && (length == part->geom.length
2a8729
+                                || (resize_partition && part->num < lpn2)))
2a8729
+                        {
2a8729
+                                /* partition is unchanged, or will be resized so nothing to do */
2a8729
+                                ok[i - 1] = 1;
2a8729
+                                continue;
2a8729
+                        }
2a8729
+                }
2a8729
+                /* Attempt to remove the partition, retrying for
2a8729
+                   up to max_sleep_seconds upon any failure due to EBUSY. */
2a8729
+                unsigned int sleep_microseconds = 10000;
2a8729
+                unsigned int max_sleep_seconds = 1;
2a8729
+                unsigned int n_sleep = (max_sleep_seconds
2a8729
+                                        * 1000000 / sleep_microseconds);
2a8729
+                do {
2a8729
+                        ok[i - 1] = remove_partition (disk, i);
2a8729
+                        errnums[i - 1] = errno;
2a8729
+                        if (ok[i - 1] || errnums[i - 1] != EBUSY)
2a8729
+                                break;
2a8729
+                        usleep (sleep_microseconds);
2a8729
+                } while (n_sleep--);
2a8729
+                if (!ok[i - 1] && errnums[i - 1] == ENXIO)
2a8729
+                        ok[i - 1] = 1; /* it already doesn't exist */
2a8729
+        }
2a8729
+        lpn = lpn2;
2a8729
+        /* don't actually add partitions for loop */
2a8729
+        if (strcmp (disk->type->name, "loop") == 0)
2a8729
+                lpn = 0;
2a8729
+        for (i = 1; i <= lpn; i++) {
2a8729
+                PedPartition *part = ped_disk_get_partition (disk, i);
2a8729
+                if (!part)
2a8729
+                        continue;
2a8729
+                unsigned long long length;
2a8729
+                unsigned long long start;
2a8729
+                /* get start and length of existing partition */
2a8729
+                if (get_partition_start_and_length(part,
2a8729
+                                                   &start, &length)
2a8729
+                    && start == part->geom.start)
2a8729
+                {
2a8729
+                        if (length == part->geom.length) {
2a8729
+                                ok[i - 1] = 1;
2a8729
+                                /* partition is unchanged, so nothing to do */
2a8729
+                                continue;
2a8729
+                        }
2a8729
+                        if (resize_partition
2a8729
+                            && start == part->geom.start)
2a8729
+                        {
2a8729
+                                /* try to resize */
2a8729
+                                if (resize_partition (disk, part)) {
2a8729
+                                        ok[i - 1] = 1;
2a8729
+                                        continue;
2a8729
+                                }
2a8729
+                        }
2a8729
+                }
2a8729
+                /* add the (possibly modified or new) partition */
2a8729
+                if (!add_partition (disk, part)) {
2a8729
+                        ok[i - 1] = 0;
2a8729
+                        errnums[i - 1] = errno;
2a8729
+                }
2a8729
+        }
2a8729
+
2a8729
+        char *bad_part_list = NULL;
2a8729
+        /* now warn about any errors */
2a8729
+        for (i = 1; i <= lpn; i++) {
2a8729
+                if (ok[i - 1] || errnums[i - 1] == ENXIO)
2a8729
+                        continue;
2a8729
+                if (bad_part_list == NULL) {
2a8729
+                        bad_part_list = malloc (lpn * 5);
2a8729
+                        if (!bad_part_list)
2a8729
+                                goto cleanup;
2a8729
+                        bad_part_list[0] = 0;
2a8729
+                }
2a8729
+                sprintf (bad_part_list + strlen (bad_part_list), "%d, ", i);
2a8729
+        }
2a8729
+        if (bad_part_list == NULL)
2a8729
+                ret = 1;
2a8729
+        else {
2a8729
+                bad_part_list[strlen (bad_part_list) - 2] = 0;
2a8729
+                if (ped_exception_throw (
2a8729
+                        PED_EXCEPTION_ERROR,
2a8729
+                        PED_EXCEPTION_IGNORE_CANCEL,
2a8729
+                        _("Partition(s) %s on %s have been written, but we have "
2a8729
+                          "been unable to inform the kernel of the change, "
2a8729
+                          "probably because it/they are in use.  As a result, "
2a8729
+                          "the old partition(s) will remain in use.  You "
2a8729
+                          "should reboot now before making further changes."),
2a8729
+                        bad_part_list, disk->dev->path) == PED_EXCEPTION_IGNORE)
2a8729
+                        ret = 1;
2a8729
+                free (bad_part_list);
2a8729
+        }
2a8729
+ cleanup:
2a8729
+        free (errnums);
2a8729
+        free (ok);
2a8729
+        return ret;
2a8729
+}
2a8729
+
2a8729
 static int
2a8729
 _have_blkpg ()
2a8729
 {
2a8729
@@ -2973,10 +3110,6 @@ _have_blkpg ()
2a8729
 static int
2a8729
 linux_disk_commit (PedDisk* disk)
2a8729
 {
2a8729
-#ifdef ENABLE_DEVICE_MAPPER
2a8729
-        if (disk->dev->type == PED_DEVICE_DM)
2a8729
-                return _dm_reread_part_table (disk);
2a8729
-#endif
2a8729
         if (disk->dev->type != PED_DEVICE_FILE) {
2a8729
 
2a8729
 		/* We now require BLKPG support.  If this assertion fails,
2a8729
-- 
2a8729
2.9.4
2a8729