summaryrefslogtreecommitdiffstats
path: root/libfdisk
diff options
context:
space:
mode:
authorKarel Zak2017-07-14 10:51:14 +0200
committerKarel Zak2017-07-14 11:34:55 +0200
commit1dd63a3b05acd7640c688f9535508417fac950d1 (patch)
tree245abf4c0da52ec0d169dddd558b960e10526c7b /libfdisk
parentpartx: move partx.h to include/ (diff)
downloadkernel-qcow2-util-linux-1dd63a3b05acd7640c688f9535508417fac950d1.tar.gz
kernel-qcow2-util-linux-1dd63a3b05acd7640c688f9535508417fac950d1.tar.xz
kernel-qcow2-util-linux-1dd63a3b05acd7640c688f9535508417fac950d1.zip
libfdisk: use BLKPG ioctls to inform kernel about changes
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>
Diffstat (limited to 'libfdisk')
-rw-r--r--libfdisk/docs/libfdisk-sections.txt1
-rw-r--r--libfdisk/src/context.c102
-rw-r--r--libfdisk/src/fdiskP.h11
-rw-r--r--libfdisk/src/libfdisk.h.in2
-rw-r--r--libfdisk/src/libfdisk.sym1
-rw-r--r--libfdisk/src/table.c69
6 files changed, 186 insertions, 0 deletions
diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt
index 337e0a643..2674ed87b 100644
--- a/libfdisk/docs/libfdisk-sections.txt
+++ b/libfdisk/docs/libfdisk-sections.txt
@@ -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
diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c
index 1ebc1b981..4e359ef6a 100644
--- a/libfdisk/src/context.c
+++ b/libfdisk/src/context.c
@@ -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:
diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h
index eec3ea877..434490edd 100644
--- a/libfdisk/src/fdiskP.h
+++ b/libfdisk/src/fdiskP.h
@@ -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,
diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in
index c00e1c072..54418cd90 100644
--- a/libfdisk/src/libfdisk.h.in
+++ b/libfdisk/src/libfdisk.h.in
@@ -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 {
diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym
index 7b43f320e..7944538cf 100644
--- a/libfdisk/src/libfdisk.sym
+++ b/libfdisk/src/libfdisk.sym
@@ -286,4 +286,5 @@ FDISK_2.30 {
FDISK_2.31 {
fdisk_reassign_device;
fdisk_device_is_used;
+ fdisk_reread_changes;
} FDISK_2.30;
diff --git a/libfdisk/src/table.c b/libfdisk/src/table.c
index f9751686b..903778426 100644
--- a/libfdisk/src/table.c
+++ b/libfdisk/src/table.c
@@ -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;
+}
+