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