]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libfdisk: enlarge partition by move start down
authorKarel Zak <kzak@redhat.com>
Mon, 8 Nov 2021 12:38:45 +0000 (13:38 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 8 Nov 2021 12:38:45 +0000 (13:38 +0100)
Now it's possible move start of the partition only when offset or
absolute value is specified. This commit implements resize to "use all
available free space before the current start".

We already support the same for end of the partition (resize to use all
free space after the partition).

Signed-off-by: Karel Zak <kzak@redhat.com>
libfdisk/src/partition.c

index 46fddc5242a609c8859fd25b0fe5d79a0db9b08c..2677cae264205dcb432b0b374c98213379451f81 100644 (file)
@@ -1041,7 +1041,7 @@ int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
        return rc;
 }
 
-static struct fdisk_partition *resize_get_by_offset(
+static struct fdisk_partition *area_by_offset(
                        struct fdisk_table *tb,
                        struct fdisk_partition *cur,
                        fdisk_sector_t off)
@@ -1064,6 +1064,67 @@ static struct fdisk_partition *resize_get_by_offset(
        return NULL;
 }
 
+static int resize_get_first_possible(
+                       struct fdisk_table *tb,
+                       struct fdisk_partition *cur,
+                       fdisk_sector_t *start)
+{
+       struct fdisk_partition *pa = NULL, *first = NULL;
+       struct fdisk_iter itr;
+
+       fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+       *start = 0;
+       DBG(TAB, ul_debugobj(tb, "checking first possible before start=%ju", (uintmax_t) cur->start));
+
+
+       while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+
+               if (pa->start > cur->start || pa == cur)
+                       break;
+
+               DBG(TAB, ul_debugobj(tb, " checking entry %p [partno=%zu start=%ju, end=%ju, size=%ju%s%s%s]",
+                       pa,
+                       fdisk_partition_get_partno(pa),
+                       (uintmax_t) fdisk_partition_get_start(pa),
+                       (uintmax_t) fdisk_partition_get_end(pa),
+                       (uintmax_t) fdisk_partition_get_size(pa),
+                       fdisk_partition_is_freespace(pa) ? " freespace" : "",
+                       fdisk_partition_is_nested(pa)    ? " nested"    : "",
+                       fdisk_partition_is_container(pa) ? " container" : ""));
+
+
+               if (!fdisk_partition_is_freespace(pa)) {
+                       DBG(TAB, ul_debugobj(tb, "  ignored (no freespace)"));
+                       first = NULL;
+                       continue;
+               }
+               if (!fdisk_partition_has_start(pa) || !fdisk_partition_has_size(pa)) {
+                       DBG(TAB, ul_debugobj(tb, "  ignored (no start/size)"));
+                       first = NULL;
+                       continue;
+               }
+               /* The current is nested, free space has to be nested within the same parent */
+               if (fdisk_partition_is_nested(cur)
+                   && pa->parent_partno != cur->parent_partno) {
+                       DBG(TAB, ul_debugobj(tb, "  ignore (nested required)"));
+                       first = NULL;
+                       continue;
+               }
+               if (pa->start + pa->size <= cur->start) {
+                       first = pa;
+                       DBG(TAB, ul_debugobj(tb, "  entry usable"));
+               }
+       }
+
+       if (first)
+               *start = first->start;
+       else
+               DBG(PART, ul_debugobj(cur, "resize: nothing usable before %ju", (uintmax_t) cur->start));
+
+       return first ? 0 : -1;
+}
+
 /*
  * Verify that area addressed by @start is freespace or the @cur[rent]
  * partition and continue to the next table entries until it's freespace, and
@@ -1211,7 +1272,16 @@ static int recount_resize(
                DBG(PART, ul_debugobj(tpl, "resize: moving start %s relative, new start: %ju",
                                tpl->movestart == FDISK_MOVE_DOWN  ? "DOWN" : "UP", (uintmax_t)start));
 
-       /* 1b) set new start - absolute number */
+       /* 1b) set new start - try freespace before the curret partition */
+       } else if (tpl->movestart == FDISK_MOVE_DOWN) {
+
+               if (resize_get_first_possible(tb, cur, &start) != 0)
+                       goto erange;
+
+               DBG(PART, ul_debugobj(tpl, "resize: moving start DOWN (first possible), new start: %ju",
+                               (uintmax_t)start));
+
+       /* 1c) set new start - absolute number */
        } else if (fdisk_partition_has_start(tpl)) {
                start = fdisk_partition_get_start(tpl);
                DBG(PART, ul_debugobj(tpl, "resize: moving start to absolute offset: %ju",
@@ -1220,7 +1290,7 @@ static int recount_resize(
 
        /* 2) verify that start is within the current partition or any freespace area */
        if (!FDISK_IS_UNDEF(start)) {
-               struct fdisk_partition *area = resize_get_by_offset(tb, cur, start);
+               struct fdisk_partition *area = area_by_offset(tb, cur, start);
 
                if (area == cur)
                        DBG(PART, ul_debugobj(tpl, "resize: start points to the current partition"));
@@ -1326,7 +1396,8 @@ int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
                return fdisk_add_partition(cxt, pa, NULL);
        }
 
-       if (pa->resize || fdisk_partition_has_start(pa) || fdisk_partition_has_size(pa)) {
+       if (pa->resize || pa->movestart
+           || fdisk_partition_has_start(pa) || fdisk_partition_has_size(pa)) {
                xpa = __copy_partition(pa);
                if (!xpa) {
                        rc = -ENOMEM;