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>
This commit is contained in:
Karel Zak 2017-07-14 10:51:14 +02:00
parent 87d04a3339
commit 1dd63a3b05
6 changed files with 186 additions and 0 deletions

View 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

View 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:

View 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,

View 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 {

View File

@ -286,4 +286,5 @@ FDISK_2.30 {
FDISK_2.31 {
fdisk_reassign_device;
fdisk_device_is_used;
fdisk_reread_changes;
} FDISK_2.30;

View 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;
}