]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libfdisk: use BLKPG ioctls to inform kernel about changes
authorKarel Zak <kzak@redhat.com>
Fri, 14 Jul 2017 08:51:14 +0000 (10:51 +0200)
committerKarel Zak <kzak@redhat.com>
Fri, 14 Jul 2017 09:34:55 +0000 (11:34 +0200)
This patch introduces fdisk_reread_changes(). The function is
less invasive alternative to fdisk_reread_partition_table().

The new function uses BLKPG ioctls for modified partitions. The
another partitions are not affected. This solution allows to
successfully use fdisks on disk where some partitions are still use
(mounted). For example if you want to resize the last partition on the
device.

Signed-off-by: Karel Zak <kzak@redhat.com>
libfdisk/docs/libfdisk-sections.txt
libfdisk/src/context.c
libfdisk/src/fdiskP.h
libfdisk/src/libfdisk.h.in
libfdisk/src/libfdisk.sym
libfdisk/src/table.c

index 337e0a643fb54e06fc1d89fabe2b991e9edc7e8e..2674ed87b5a7ac55eefdf00e5e937b5197afabe0 100644 (file)
@@ -325,6 +325,7 @@ fdisk_new_context
 fdisk_new_nested_context
 FDISK_PLURAL
 fdisk_ref_context
+fdisk_reread_changes
 fdisk_reread_partition_table
 fdisk_set_first_lba
 fdisk_set_last_lba
index 1ebc1b981a9927d4668368ebddde362453551a50..4e359ef6a59b0da9baa544fd74b02f97237131c2 100644 (file)
@@ -3,6 +3,7 @@
 #endif
 
 #include "blkdev.h"
+#include "partx.h"
 #include "loopdev.h"
 #include "fdiskP.h"
 
@@ -734,6 +735,107 @@ int fdisk_reread_partition_table(struct fdisk_context *cxt)
        return 0;
 }
 
+static inline int add_to_partitions_array(
+                       struct fdisk_partition ***ary,
+                       struct fdisk_partition *pa,
+                       size_t *n, size_t nmax)
+{
+       if (!*ary) {
+               *ary = calloc(nmax, sizeof(struct fdisk_partition *));
+               if (!*ary)
+                       return -ENOMEM;
+       }
+       (*ary)[*n] = pa;
+       (*n)++;
+       return 0;
+}
+
+/**
+ * fdisk_reread_changes:
+ * @cxt: context
+ * @org: original layout (on disk)
+ *
+ * Like fdisk_reread_partition_table() but don't forces kernel re-read all
+ * partition table. The BLKPG_* ioctls are used for individual partitions. The
+ * advantage is that unmodified partitions maybe mounted.
+ *
+ * Returns: <0 on error, or 0.
+ */
+int fdisk_reread_changes(struct fdisk_context *cxt, struct fdisk_table *org)
+{
+       struct fdisk_table *tb = NULL;
+       struct fdisk_iter itr;
+       struct fdisk_partition *pa;
+       struct fdisk_partition **rem = NULL, **add = NULL, **upd = NULL;
+       int change, rc = 0, err = 0;
+       size_t nparts, i, nadds = 0, nupds = 0, nrems = 0;
+
+       DBG(CXT, ul_debugobj(cxt, "rereading changes"));
+
+       fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+       /* the current layout */
+       fdisk_get_partitions(cxt, &tb);
+       /* maximal number of partitions */
+       nparts = max(fdisk_table_get_nents(tb), fdisk_table_get_nents(org));
+
+       while (fdisk_diff_tables(org, tb, &itr, &pa, &change) == 0) {
+               if (change == FDISK_DIFF_UNCHANGED)
+                       continue;
+               switch (change) {
+               case FDISK_DIFF_REMOVED:
+                       rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
+                       break;
+               case FDISK_DIFF_ADDED:
+                       rc = add_to_partitions_array(&add, pa, &nadds, nparts);
+                       break;
+               case FDISK_DIFF_RESIZED:
+                       rc = add_to_partitions_array(&upd, pa, &nupds, nparts);
+                       break;
+               case FDISK_DIFF_MOVED:
+                       rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
+                       rc = add_to_partitions_array(&add, pa, &nadds, nparts);
+                       break;
+               }
+               if (rc != 0)
+                       goto done;
+       }
+
+       for (i = 0; i < nrems; i++) {
+               pa = rem[i];
+               DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_DEL_PARTITION", pa->partno));
+               if (partx_del_partition(cxt->dev_fd, pa->partno + 1) != 0) {
+                       fdisk_warn(cxt, _("Failed to remove partition %zu from system"), pa->partno + 1); 
+                       err++;
+               }
+       }
+       for (i = 0; i < nupds; i++) {
+               pa = upd[i];
+               DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_RESIZE_PARTITION", pa->partno));
+               if (partx_resize_partition(cxt->dev_fd, pa->partno + 1, pa->start, pa->size) != 0) {
+                       fdisk_warn(cxt, _("Failed to update system information about partition %zu"), pa->partno + 1); 
+                       err++;
+               }
+       }
+       for (i = 0; i < nadds; i++) {
+               pa = add[i];
+               DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_ADD_PARTITION", pa->partno));
+               if (partx_add_partition(cxt->dev_fd, pa->partno + 1, pa->start, pa->size) != 0) {
+                       fdisk_warn(cxt, _("Failed to add partition %zu to system"), pa->partno + 1); 
+                       err++;
+               }
+       }
+       if (err)
+               fdisk_info(cxt, _(
+                       "The kernel still uses the old partitions. The new "
+                       "table will be used at the next reboot. "));
+done:
+       free(rem);
+       free(add);
+       free(upd);
+       fdisk_unref_table(tb);
+       return rc;
+}
 
 /**
  * fdisk_device_is_used:
index eec3ea877fd4926d23440dca340ed8a7039add82..434490edd5fffd64125a8eec457a257e3919285f 100644 (file)
@@ -412,6 +412,17 @@ struct fdisk_context {
        struct fdisk_script     *script;        /* what we want to follow */
 };
 
+/* table */
+enum {
+       FDISK_DIFF_UNCHANGED = 0,
+       FDISK_DIFF_REMOVED,
+       FDISK_DIFF_ADDED,
+       FDISK_DIFF_MOVED,
+       FDISK_DIFF_RESIZED
+};
+extern int fdisk_diff_tables(struct fdisk_table *a, struct fdisk_table *b,
+                               struct fdisk_iter *itr,
+                               struct fdisk_partition **res, int *change);
 
 /* context.c */
 extern int __fdisk_switch_label(struct fdisk_context *cxt,
index c00e1c072f7ce308956063b5470245cf92630451..54418cd90c4c7a00e084be45d9b8e9d57d1a602a 100644 (file)
@@ -459,6 +459,7 @@ extern int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_par
 extern int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb);
 extern int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb);
 
+
 extern int fdisk_table_wrong_order(struct fdisk_table *tb);
 extern int fdisk_table_sort_partitions(struct fdisk_table *tb,
                        int (*cmp)(struct fdisk_partition *,
@@ -503,6 +504,7 @@ int fdisk_has_user_device_properties(struct fdisk_context *cxt);
 int fdisk_reset_alignment(struct fdisk_context *cxt);
 int fdisk_reset_device_properties(struct fdisk_context *cxt);
 int fdisk_reread_partition_table(struct fdisk_context *cxt);
+int fdisk_reread_changes(struct fdisk_context *cxt, struct fdisk_table *org);
 
 /* iter.c */
 enum {
index 7b43f320e73530d54e75466fc3f2f46f2ec825d1..7944538cf4eae00be246aec0f6828b49f11d5c3d 100644 (file)
@@ -286,4 +286,5 @@ FDISK_2.30 {
 FDISK_2.31 {
        fdisk_reassign_device;
        fdisk_device_is_used;
+       fdisk_reread_changes;
 } FDISK_2.30;
index f9751686bdc4610faa1566504452195367257063..90377842673bfe112eef3bbbe0741bbd173e9ae9 100644 (file)
@@ -709,3 +709,72 @@ int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb)
        return rc;
 }
 
+int fdisk_diff_tables(struct fdisk_table *a, struct fdisk_table *b,
+                     struct fdisk_iter *itr,
+                     struct fdisk_partition **res, int *change)
+{
+       struct fdisk_partition *pa, *pb;
+       int rc = 1;
+
+       assert(itr);
+       assert(res);
+       assert(change);
+
+       DBG(TAB, ul_debugobj(a, "table diff [new table=%p]", b));
+
+       if (a && (itr->head == NULL || itr->head == &a->parts)) {
+               DBG(TAB, ul_debugobj(a, " scanning old table"));
+               do {
+                       rc = fdisk_table_next_partition(a, itr, &pa);
+                       if (rc != 0)
+                               break;
+               } while (!fdisk_partition_has_partno(pa));
+       }
+
+       if (rc == 1 && b) {
+               DBG(TAB, ul_debugobj(a, " scanning new table"));
+               if (itr->head != &b->parts) {
+                       DBG(TAB, ul_debugobj(a, "  initialize to TAB=%p", b));
+                       fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
+               }
+
+               while (fdisk_table_next_partition(b, itr, &pb) == 0) {
+                       if (!fdisk_partition_has_partno(pb))
+                               continue;
+                       if (a == NULL ||
+                           fdisk_table_get_partition_by_partno(a, pb->partno) == NULL) {
+                               DBG(TAB, ul_debugobj(a, " #%zu ADDED", pb->partno));
+                               *change = FDISK_DIFF_ADDED;
+                               *res = pb;
+                               return 0;
+                       }
+               }
+       }
+
+       if (rc) {
+               DBG(TAB, ul_debugobj(a, "table diff done [rc=%d]", rc));
+               return rc;      /* error or done */
+       }
+
+       pb = fdisk_table_get_partition_by_partno(b, pa->partno);
+
+       if (!pb) {
+               DBG(TAB, ul_debugobj(a, " #%zu REMOVED", pa->partno));
+               *change = FDISK_DIFF_REMOVED;
+               *res = pa;
+       } else if (pb->start != pa->start) {
+               DBG(TAB, ul_debugobj(a, " #%zu MOVED", pb->partno));
+               *change = FDISK_DIFF_MOVED;
+               *res = pb;
+       } else if (pb->size != pa->size) {
+               DBG(TAB, ul_debugobj(a, " #%zu RESIZED", pb->partno));
+               *change = FDISK_DIFF_RESIZED;
+               *res = pb;
+       } else {
+               DBG(TAB, ul_debugobj(a, " #%zu UNCHANGED", pb->partno));
+               *change = FDISK_DIFF_UNCHANGED;
+               *res = pa;
+       }
+       return 0;
+}
+