]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - libfdisk/src/context.c
libfdisk: fix variable shadowing
[thirdparty/util-linux.git] / libfdisk / src / context.c
index 5385baaae7861546ecc1f1349439016ded7a501d..770aa4d5203ec8f7f2199a12274a4957a0854686 100644 (file)
@@ -2,23 +2,28 @@
 # include <blkid.h>
 #endif
 
+#include "blkdev.h"
+#ifdef __linux__
+# include "partx.h"
+#endif
+#include "loopdev.h"
 #include "fdiskP.h"
 
 
 /**
  * SECTION: context
- * @title: libfdisk handler
- * @short_description: stores infor about device, labels etc.
+ * @title: Context
+ * @short_description: stores info about device, labels etc.
  *
  * The library distinguish between three types of partitioning objects.
  *
- * on-disk data
+ * on-disk label data
  *    - disk label specific
  *    - probed and read  by disklabel drivers when assign device to the context
  *      or when switch to another disk label type
  *    - only fdisk_write_disklabel() modify on-disk data
  *
- * in-memory data
+ * in-memory label data
  *    - generic data and disklabel specific data stored in struct fdisk_label
  *    - all partitioning operations are based on in-memory data only
  *
  *    - provides abstraction to present partitions to users
  *    - fdisk_partition is possible to gather to fdisk_table container
  *    - used as unified template for new partitions
+ *    - used (with fdisk_table) in fdisk scripts
  *    - the struct fdisk_partition is always completely independent object and
  *      any change to the object has no effect to in-memory (or on-disk) label data
+ *
+ * Don't forget to inform kernel about changes by fdisk_reread_partition_table()
+ * or more smart fdisk_reread_changes().
  */
 
 /**
@@ -47,6 +56,8 @@ struct fdisk_context *fdisk_new_context(void)
        cxt->dev_fd = -1;
        cxt->refcount = 1;
 
+       INIT_LIST_HEAD(&cxt->wipes);
+
        /*
         * Allocate label specific structs.
         *
@@ -59,6 +70,8 @@ struct fdisk_context *fdisk_new_context(void)
        cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
        cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
 
+       bindtextdomain(LIBFDISK_TEXTDOMAIN, LOCALEDIR);
+
        return cxt;
 }
 
@@ -94,14 +107,19 @@ static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
        cxt->user_log_sector =  parent->user_log_sector;
        cxt->user_pyh_sector =  parent->user_pyh_sector;
 
-       /* parent <--> nested independent setting, initialize for new nested 
+       /* parent <--> nested independent setting, initialize for new nested
         * contexts only */
        if (isnew) {
                cxt->listonly = parent->listonly;
                cxt->display_details =  parent->display_details;
                cxt->display_in_cyl_units = parent->display_in_cyl_units;
+               cxt->protect_bootbits = parent->protect_bootbits;
        }
 
+       free(cxt->dev_model);
+       cxt->dev_model = NULL;
+       cxt->dev_model_probed = 0;
+
        free(cxt->dev_path);
        cxt->dev_path = NULL;
 
@@ -111,6 +129,8 @@ static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
                        return -ENOMEM;
        }
 
+       INIT_LIST_HEAD(&cxt->wipes);
+
        return 0;
 }
 
@@ -143,19 +163,22 @@ struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
        if (!cxt)
                return NULL;
 
-       DBG(CXT, ul_debugobj(parent, "alloc nested [%p]", cxt));
+       DBG(CXT, ul_debugobj(parent, "alloc nested [%p] [name=%s]", cxt, name));
        cxt->refcount = 1;
 
        fdisk_ref_context(parent);
        cxt->parent = parent;
 
-       if (init_nested_from_parent(cxt, 1) != 0)
+       if (init_nested_from_parent(cxt, 1) != 0) {
+               cxt->parent = NULL;
+               fdisk_unref_context(cxt);
                return NULL;
+       }
 
        if (name) {
-               if (strcmp(name, "bsd") == 0)
+               if (strcasecmp(name, "bsd") == 0)
                        lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
-               else if (strcmp(name, "dos") == 0)
+               else if (strcasecmp(name, "dos") == 0 || strcasecmp(name, "mbr") == 0)
                        lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
        }
 
@@ -197,6 +220,10 @@ void fdisk_ref_context(struct fdisk_context *cxt)
  *
  * If no @name specified then returns the current context label.
  *
+ * The label is allocated and maintained within the context #cxt. There is
+ * nothing like reference counting for labels, you cannot deallocate the
+ * label.
+ *
  * Returns: label struct or NULL in case of error.
  */
 struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
@@ -207,10 +234,12 @@ struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
 
        if (!name)
                return cxt->label;
+       else if (strcasecmp(name, "mbr") == 0)
+               name = "dos";
 
        for (i = 0; i < cxt->nlabels; i++)
                if (cxt->labels[i]
-                   && strcmp(cxt->labels[i]->name, name) == 0)
+                   && strcasecmp(cxt->labels[i]->name, name) == 0)
                        return cxt->labels[i];
 
        DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
@@ -280,6 +309,8 @@ int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
        }
        cxt->label = lb;
        DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
+
+       fdisk_apply_label_device_properties(cxt);
        return 0;
 }
 
@@ -294,6 +325,137 @@ int fdisk_has_label(struct fdisk_context *cxt)
        return cxt && cxt->label;
 }
 
+/**
+ * fdisk_has_protected_bootbits:
+ * @cxt: fdisk context
+ *
+ * Returns: return 1 if boot bits protection enabled.
+ */
+int fdisk_has_protected_bootbits(struct fdisk_context *cxt)
+{
+       return cxt && cxt->protect_bootbits;
+}
+
+/**
+ * fdisk_enable_bootbits_protection:
+ * @cxt: fdisk context
+ * @enable: 1 or 0
+ *
+ * The library zeroizes all the first sector when create a new disk label by
+ * default.  This function allows to control this behavior. For now it's
+ * supported for MBR and GPT.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable)
+{
+       if (!cxt)
+               return -EINVAL;
+       cxt->protect_bootbits = enable ? 1 : 0;
+       return 0;
+}
+/**
+ * fdisk_disable_dialogs
+ * @cxt: fdisk context
+ * @disable: 1 or 0
+ *
+ * The library uses dialog driven partitioning by default.
+ *
+ * Returns: 0 on success, < 0 on error.
+ *
+ * Since: 2.31
+ */
+int fdisk_disable_dialogs(struct fdisk_context *cxt, int disable)
+{
+       if (!cxt)
+               return -EINVAL;
+
+       cxt->no_disalogs = disable;
+       return 0;
+}
+
+/**
+ * fdisk_has_dialogs
+ * @cxt: fdisk context
+ *
+ * See fdisk_disable_dialogs()
+ *
+ * Returns: 1 if dialog driven partitioning enabled (default), or 0.
+ *
+ * Since: 2.31
+ */
+int fdisk_has_dialogs(struct fdisk_context *cxt)
+{
+       return cxt->no_disalogs == 0;
+}
+
+/**
+ * fdisk_enable_wipe
+ * @cxt: fdisk context
+ * @enable: 1 or 0
+ *
+ * The library removes all PT/filesystem/RAID signatures before it writes
+ * partition table. The probing area where it looks for signatures is from
+ * the begin of the disk. The device is wiped by libblkid.
+ *
+ * See also fdisk_wipe_partition().
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_wipe(struct fdisk_context *cxt, int enable)
+{
+       if (!cxt)
+               return -EINVAL;
+
+       fdisk_set_wipe_area(cxt, 0, cxt->total_sectors, enable);
+       return 0;
+}
+
+/**
+ * fdisk_has_wipe
+ * @cxt: fdisk context
+ *
+ * Returns the current wipe setting. See fdisk_enable_wipe().
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_has_wipe(struct fdisk_context *cxt)
+{
+       if (!cxt)
+               return 0;
+
+       return fdisk_has_wipe_area(cxt, 0, cxt->total_sectors);
+}
+
+
+/**
+ * fdisk_get_collision
+ * @cxt: fdisk context
+ *
+ * Returns: name of the filesystem or RAID detected on the device or NULL.
+ */
+const char *fdisk_get_collision(struct fdisk_context *cxt)
+{
+       return cxt->collision;
+}
+
+/**
+ * fdisk_is_ptcollision:
+ * @cxt: fdisk context
+ *
+ * The collision detected by libblkid (usually another partition table). Note
+ * that libfdisk does not support all partitions tables, so fdisk_has_label()
+ * may return false, but fdisk_is_ptcollision() may return true.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_is_ptcollision(struct fdisk_context *cxt)
+{
+       return cxt->pt_collision;
+}
+
 /**
  * fdisk_get_npartitions:
  * @cxt: context
@@ -325,7 +487,7 @@ int fdisk_has_label(struct fdisk_context *cxt)
  * </informalexample>
  *
  * Note that the recommended way to list partitions is to use
- * fdisk_get_partitions() and struct fdisk_table than ask disk driver for each
+ * fdisk_get_partitions() and struct fdisk_table then ask disk driver for each
  * individual partitions.
  *
  * Returns: maximal number of partitions for the current label.
@@ -342,13 +504,13 @@ size_t fdisk_get_npartitions(struct fdisk_context *cxt)
  *
  * See also fdisk_is_label() macro in libfdisk.h.
  *
- * Returns: return 1 if the current label is @l
+ * Returns: return 1 if the current label is @id
  */
 int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
 {
        assert(cxt);
 
-       return cxt->label && fdisk_label_get_type(cxt->label) == id;
+       return cxt->label && (unsigned)fdisk_label_get_type(cxt->label) == id;
 }
 
 /**
@@ -379,7 +541,7 @@ static void reset_context(struct fdisk_context *cxt)
                        free(cxt->firstsector);
        } else {
                /* we close device only in primary context */
-               if (cxt->dev_fd > -1)
+               if (cxt->dev_fd > -1 && cxt->private_fd)
                        close(cxt->dev_fd);
                free(cxt->firstsector);
        }
@@ -387,7 +549,17 @@ static void reset_context(struct fdisk_context *cxt)
        free(cxt->dev_path);
        cxt->dev_path = NULL;
 
+       free(cxt->dev_model);
+       cxt->dev_model = NULL;
+       cxt->dev_model_probed = 0;
+
+       free(cxt->collision);
+       cxt->collision = NULL;
+
+       memset(&cxt->dev_st, 0, sizeof(cxt->dev_st));
+
        cxt->dev_fd = -1;
+       cxt->private_fd = 0;
        cxt->firstsector = NULL;
        cxt->firstsector_bufsz = 0;
 
@@ -397,96 +569,27 @@ static void reset_context(struct fdisk_context *cxt)
        cxt->script = NULL;
 
        cxt->label = NULL;
-}
 
-/*
- * This function prints a warning if the device is not wiped (e.g. wipefs(8).
- * Please don't call this function if there is already a PT.
- *
- * Returns: 0 if nothing found, < 0 on error, 1 if found a signature
- */
-static int warn_wipe(struct fdisk_context *cxt)
-{
-#ifdef HAVE_LIBBLKID
-       blkid_probe pr;
-#endif
-       int rc = 0;
-
-       assert(cxt);
-
-       if (fdisk_has_label(cxt) || cxt->dev_fd < 0)
-               return -EINVAL;
-#ifdef HAVE_LIBBLKID
-       DBG(CXT, ul_debugobj(cxt, "wipe check: initialize libblkid prober"));
-
-       pr = blkid_new_probe();
-       if (!pr)
-               return -ENOMEM;
-       rc = blkid_probe_set_device(pr, cxt->dev_fd, 0, 0);
-       if (rc)
-               return rc;
-
-       blkid_probe_enable_superblocks(pr, 1);
-       blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
-       blkid_probe_enable_partitions(pr, 1);
-
-       /* we care about the first found FS/raid, so don't call blkid_do_probe()
-        * in loop or don't use blkid_do_fullprobe() ... */
-       rc = blkid_do_probe(pr);
-       if (rc == 0) {
-               const char *name = NULL;
-
-               if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0 ||
-                   blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) {
-                       fdisk_warnx(cxt, _(
-                               "%s: device contains a valid '%s' signature; it is "
-                               "strongly recommended to wipe the device with "
-                               "wipefs(8) if this is unexpected, in order to "
-                               "avoid possible collisions"), cxt->dev_path, name);
-                       rc = 1;
-               }
-       }
-
-       blkid_free_probe(pr);
-#endif
-       return rc;
+       fdisk_free_wipe_areas(cxt);
 }
 
-/**
- * fdisk_assign_device:
- * @cxt: context
- * @fname: path to the device to be handled
- * @readonly: how to open the device
- *
- * Open the device, discovery topology, geometry, detect disklabel and switch
- * the current label driver to reflect the probing result.
- *
- * Note that this function resets all generic setting in context. If the @cxt
- * is nested context then the device is assigned to the parental context and
- * necessary properties are copied to the @cxt. The change is propagated in
- * child->parent direction only. It's impossible to use a different device for
- * primary and nested contexts.
- *
- * Returns: 0 on success, < 0 on error.
- */
-int fdisk_assign_device(struct fdisk_context *cxt,
-                       const char *fname, int readonly)
+/* fdisk_assign_device() body */
+static int fdisk_assign_fd(struct fdisk_context *cxt, int fd,
+                       const char *fname, int readonly, int privfd)
 {
-       int fd;
-
-       DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
        assert(cxt);
+       assert(fd >= 0);
 
        /* redirect request to parent */
        if (cxt->parent) {
                int rc, org = fdisk_is_listonly(cxt->parent);
 
                /* assign_device() is sensitive to "listonly" mode, so let's
-                * follow the current context setting for the parent to avoid 
+                * follow the current context setting for the parent to avoid
                 * unwanted extra warnings. */
                fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
 
-               rc = fdisk_assign_device(cxt->parent, fname, readonly);
+               rc = fdisk_assign_fd(cxt->parent, fd, fname, readonly, privfd);
                fdisk_enable_listonly(cxt->parent, org);
 
                if (!rc)
@@ -498,41 +601,109 @@ int fdisk_assign_device(struct fdisk_context *cxt,
 
        reset_context(cxt);
 
-       fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
-       if (fd < 0)
-               return -errno;
+       if (fstat(fd, &cxt->dev_st) != 0)
+               goto fail;
 
        cxt->readonly = readonly;
        cxt->dev_fd = fd;
-       cxt->dev_path = strdup(fname);
+       cxt->private_fd = privfd;
+       cxt->dev_path = fname ? strdup(fname) : NULL;
        if (!cxt->dev_path)
                goto fail;
 
        fdisk_discover_topology(cxt);
        fdisk_discover_geometry(cxt);
 
+       fdisk_apply_user_device_properties(cxt);
+
        if (fdisk_read_firstsector(cxt) < 0)
                goto fail;
 
-       /* detect labels and apply labes specific stuff (e.g geomery)
-        * to the context */
        fdisk_probe_labels(cxt);
-
-       /* let's apply user geometry *after* label prober
-        * to make it possible to override in-label setting */
-       fdisk_apply_user_device_properties(cxt);
+       fdisk_apply_label_device_properties(cxt);
 
        /* warn about obsolete stuff on the device if we aren't in
         * list-only mode and there is not PT yet */
-       if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt))
-               warn_wipe(cxt);
+       if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt)
+           && fdisk_check_collisions(cxt) < 0)
+               goto fail;
 
        DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
                              fname, readonly ? "READ-ONLY" : "READ-WRITE"));
        return 0;
 fail:
-       DBG(CXT, ul_debugobj(cxt, "failed to assign device"));
-       return -errno;
+       {
+               int rc = -errno;
+               DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc));
+               return rc;
+       }
+}
+
+/**
+ * fdisk_assign_device:
+ * @cxt: context
+ * @fname: path to the device to be handled
+ * @readonly: how to open the device
+ *
+ * Open the device, discovery topology, geometry, detect disklabel, check for
+ * collisions and switch the current label driver to reflect the probing
+ * result.
+ *
+ * If in standard mode (!= non-listonly mode) then also detects for collisions.
+ * The result is accessible by fdisk_get_collision() and
+ * fdisk_is_ptcollision().  The collision (e.g. old obsolete PT) may be removed
+ * by fdisk_enable_wipe().  Note that new PT and old PT may be on different
+ * locations.
+ *
+ * Note that this function resets all generic setting in context.
+ *
+ * If the @cxt is nested context (necessary for example to edit BSD or PMBR)
+ * then the device is assigned to the parental context and necessary properties
+ * are copied to the @cxt. The change is propagated in child->parent direction
+ * only. It's impossible to use a different device for primary and nested
+ * contexts.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_assign_device(struct fdisk_context *cxt,
+                       const char *fname, int readonly)
+{
+       int fd, rc;
+
+       DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
+       assert(cxt);
+
+       fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
+       if (fd < 0) {
+               rc = -errno;
+               DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc));
+               return rc;
+       }
+
+       rc = fdisk_assign_fd(cxt, fd, fname, readonly, 1);
+       if (rc)
+               close(fd);
+       return rc;
+}
+
+/**
+ * fdisk_assign_device_by_fd:
+ * @cxt: context
+ * @fd: device file descriptor
+ * @fname: path to the device (used for dialogs, debugging, partition names, ...)
+ * @readonly: how to use the device
+ *
+ * Like fdisk_assign_device(), but caller is responsible to open and close the
+ * device. The library only fsync() the device on fdisk_deassign_device().
+ *
+ * The device has to be open O_RDWR on @readonly=0.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_assign_device_by_fd(struct fdisk_context *cxt, int fd,
+                       const char *fname, int readonly)
+{
+       return fdisk_assign_fd(cxt, fd, fname, readonly, 0);
 }
 
 /**
@@ -540,7 +711,7 @@ fail:
  * @cxt: context
  * @nosync: disable fsync()
  *
- * Close device and call fsync(). If the @cxt is nested context than the
+ * Close device and call fsync(). If the @cxt is nested context then the
  * request is redirected to the parent.
  *
  * Returns: 0 on success, < 0 on error.
@@ -558,15 +729,21 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
                return rc;
        }
 
-       if (cxt->readonly)
+       DBG(CXT, ul_debugobj(cxt, "de-assigning device %s", cxt->dev_path));
+
+       if (cxt->readonly && cxt->private_fd)
                close(cxt->dev_fd);
        else {
-               if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
+               if (fsync(cxt->dev_fd)) {
+                       fdisk_warn(cxt, _("%s: fsync device failed"),
+                                       cxt->dev_path);
+                       return -errno;
+               }
+               if (cxt->private_fd && close(cxt->dev_fd)) {
                        fdisk_warn(cxt, _("%s: close device failed"),
                                        cxt->dev_path);
                        return -errno;
                }
-
                if (!nosync) {
                        fdisk_info(cxt, _("Syncing disks."));
                        sync();
@@ -575,12 +752,241 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
 
        free(cxt->dev_path);
        cxt->dev_path = NULL;
-
        cxt->dev_fd = -1;
 
        return 0;
 }
 
+/**
+ * fdisk_reassign_device:
+ * @cxt: context
+ *
+ * This function is "hard reset" of the context and it does not write anything
+ * to the device. All in-memory changes associated with the context will be
+ * lost. It's recommended to use this function after some fatal problem when the
+ * context (and label specific driver) is in an undefined state.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_reassign_device(struct fdisk_context *cxt)
+{
+       char *devname;
+       int rdonly, rc, fd, privfd;
+
+       assert(cxt);
+       assert(cxt->dev_fd >= 0);
+
+       DBG(CXT, ul_debugobj(cxt, "re-assigning device %s", cxt->dev_path));
+
+       devname = strdup(cxt->dev_path);
+       if (!devname)
+               return -ENOMEM;
+
+       rdonly = cxt->readonly;
+       fd = cxt->dev_fd;
+       privfd = cxt->private_fd;
+
+       fdisk_deassign_device(cxt, 1);
+
+       if (privfd)
+               /* reopen and assign */
+               rc = fdisk_assign_device(cxt, devname, rdonly);
+       else
+               /* assign only */
+               rc = fdisk_assign_fd(cxt, fd, devname, rdonly, privfd);
+
+       free(devname);
+       return rc;
+}
+
+/**
+ * fdisk_reread_partition_table:
+ * @cxt: context
+ *
+ * Force *kernel* to re-read partition table on block devices.
+ *
+ * Returns: 0 on success, < 0 in case of error.
+ */
+int fdisk_reread_partition_table(struct fdisk_context *cxt)
+{
+       int i = 0;
+
+       assert(cxt);
+       assert(cxt->dev_fd >= 0);
+
+       if (!S_ISBLK(cxt->dev_st.st_mode))
+               return 0;
+       else {
+               DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl"));
+               sync();
+#ifdef BLKRRPART
+               fdisk_info(cxt, _("Calling ioctl() to re-read partition table."));
+               i = ioctl(cxt->dev_fd, BLKRRPART);
+#else
+               errno = ENOSYS;
+               i = 1;
+#endif
+       }
+
+       if (i) {
+               fdisk_warn(cxt, _("Re-reading the partition table failed."));
+               fdisk_info(cxt, _(
+                       "The kernel still uses the old table. The "
+                       "new table will be used at the next reboot "
+                       "or after you run partprobe(8) or kpartx(8)."));
+               return -errno;
+       }
+
+       return 0;
+}
+
+#ifdef __linux__
+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;
+}
+#endif
+
+/**
+ * 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.
+ *
+ * The function behavies like fdisk_reread_partition_table() on systems where
+ * are no available BLKPG_* ioctls.
+ *
+ * Returns: <0 on error, or 0.
+ */
+#ifdef __linux__
+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);
+                       if (!rc)
+                               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;
+}
+#else
+int fdisk_reread_changes(struct fdisk_context *cxt,
+                        struct fdisk_table *org __attribute__((__unused__))) {
+       return fdisk_reread_partition_table(cxt);
+}
+#endif
+
+/**
+ * fdisk_device_is_used:
+ * @cxt: context
+ *
+ * On systems where is no BLKRRPART ioctl the function returns zero and
+ * sets errno to ENOSYS.
+ *
+ * Returns: 1 if the device assigned to the context is used by system, or 0.
+ */
+int fdisk_device_is_used(struct fdisk_context *cxt)
+{
+       int rc = 0;
+
+       assert(cxt);
+       assert(cxt->dev_fd >= 0);
+
+       errno = 0;
+
+#ifdef BLKRRPART
+       /* it seems kernel always return EINVAL for BLKRRPART on loopdevices */
+       if (S_ISBLK(cxt->dev_st.st_mode)
+           && major(cxt->dev_st.st_rdev) != LOOPDEV_MAJOR) {
+               DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl"));
+               rc = ioctl(cxt->dev_fd, BLKRRPART) != 0;
+       }
+#else
+       errno = ENOSYS;
+#endif
+       DBG(CXT, ul_debugobj(cxt, "device used: %s [errno=%d]", rc ? "TRUE" : "FALSE", errno));
+       return rc;
+}
+
 /**
  * fdisk_is_readonly:
  * @cxt: context
@@ -593,6 +999,20 @@ int fdisk_is_readonly(struct fdisk_context *cxt)
        return cxt->readonly;
 }
 
+/**
+ * fdisk_is_regfile:
+ * @cxt: context
+ *
+ * Since: 2.30
+ *
+ * Returns: 1 if open file descriptor is regular file rather than a block device.
+ */
+int fdisk_is_regfile(struct fdisk_context *cxt)
+{
+       assert(cxt);
+       return S_ISREG(cxt->dev_st.st_mode);
+}
+
 /**
  * fdisk_unref_context:
  * @cxt: fdisk context
@@ -601,7 +1021,7 @@ int fdisk_is_readonly(struct fdisk_context *cxt)
  */
 void fdisk_unref_context(struct fdisk_context *cxt)
 {
-       int i;
+       unsigned i;
 
        if (!cxt)
                return;
@@ -629,31 +1049,11 @@ void fdisk_unref_context(struct fdisk_context *cxt)
        }
 }
 
-/**
- * fdisk_set_ask:
- * @cxt: context
- * @ask_cb: callback
- * @data: callback data
- *
- * Set callback for dialog driven partitioning and library warnings/errors.
- *
- * Returns: 0 on success, < 0 on error.
- */
-int fdisk_set_ask(struct fdisk_context *cxt,
-               int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
-               void *data)
-{
-       assert(cxt);
-
-       cxt->ask_cb = ask_cb;
-       cxt->ask_data = data;
-       return 0;
-}
 
 /**
  * fdisk_enable_details:
  * @cxt: context
- * @enable: true/flase
+ * @enable: true/false
  *
  * Enables or disables "details" display mode. This function has effect to
  * fdisk_partition_to_string() function.
@@ -682,7 +1082,7 @@ int fdisk_is_details(struct fdisk_context *cxt)
 /**
  * fdisk_enable_listonly:
  * @cxt: context
- * @enable: true/flase
+ * @enable: true/false
  *
  * Just list partition only, don't care about another details, mistakes, ...
  *
@@ -716,7 +1116,7 @@ int fdisk_is_listonly(struct fdisk_context *cxt)
  * This is pure shit, unfortunately for example Sun addresses begin of the
  * partition by cylinders...
  *
- * Returns: 0 on succes, <0 on error.
+ * Returns: 0 on success, <0 on error.
  */
 int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
 {
@@ -740,6 +1140,7 @@ int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
 /**
  * fdisk_get_unit:
  * @cxt: context
+ * @n: FDISK_PLURAL or FDISK_SINGULAR
  *
  * Returns: unit name.
  */
@@ -754,9 +1155,9 @@ const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
 
 /**
  * fdisk_use_cylinders:
- * @@cxt: context
+ * @cxt: context
  *
- * Returns 1 if user wants to display in cylinders.
+ * Returns: 1 if user wants to display in cylinders.
  */
 int fdisk_use_cylinders(struct fdisk_context *cxt)
 {
@@ -869,7 +1270,7 @@ unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
  *
  * Returns: first possible LBA on disk for data partitions.
  */
-sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
 {
        assert(cxt);
        return cxt->first_lba;
@@ -881,7 +1282,7 @@ sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
  * @lba: first possible logical sector for data
  *
  * It's strongly recommended to use the default library setting. The first LBA
- * is always reseted by fdisk_assign_device(), fdisk_override_geometry()
+ * is always reset by fdisk_assign_device(), fdisk_override_geometry()
  * and fdisk_reset_alignment(). This is very low level function and library
  * does not check if your setting makes any sense.
  *
@@ -889,9 +1290,14 @@ sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
  * partition tables like GPT protective MBR or hybrid partition tables on
  * bootable media where the first partition may start on very crazy offsets.
  *
+ * Note that this function changes only runtime information. It does not update
+ * any range in on-disk partition table. For example GPT Header contains First
+ * and Last usable LBA fields. These fields are not updated by this function.
+ * Be careful.
+ *
  * Returns: 0 on success, <0 on error.
  */
-sector_t fdisk_set_first_lba(struct fdisk_context *cxt, sector_t lba)
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
 {
        assert(cxt);
        DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
@@ -908,7 +1314,7 @@ sector_t fdisk_set_first_lba(struct fdisk_context *cxt, sector_t lba)
  *
  * Returns: last possible LBA on device
  */
-sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
 {
        return cxt->last_lba;
 }
@@ -919,25 +1325,54 @@ sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
  * @lba: last possible logical sector
  *
  * It's strongly recommended to use the default library setting. The last LBA
- * is always reseted by fdisk_assign_device(), fdisk_override_geometry() and
+ * is always reset by fdisk_assign_device(), fdisk_override_geometry() and
  * fdisk_reset_alignment().
  *
  * The default is number of sectors on the device, but maybe modified by the
- * current disklabel driver (for example GPT uses and of disk for backup
+ * current disklabel driver (for example GPT uses the end of disk for backup
  * header, so last_lba is smaller than total number of sectors).
  *
  * Returns: 0 on success, <0 on error.
  */
-sector_t fdisk_set_last_lba(struct fdisk_context *cxt, sector_t lba)
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
 {
        assert(cxt);
 
-       if (lba > cxt->total_sectors - 1 && lba < 1)
+       if (lba > cxt->total_sectors - 1 || lba < 1)
                return -ERANGE;
        cxt->last_lba = lba;
        return 0;
 }
 
+/**
+ * fdisk_set_size_unit:
+ * @cxt: fdisk context
+ * @unit: FDISK_SIZEUNIT_*
+ *
+ * Sets unit for SIZE output field (see fdisk_partition_to_string()).
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_set_size_unit(struct fdisk_context *cxt, int unit)
+{
+       assert(cxt);
+       cxt->sizeunit = unit;
+       return 0;
+}
+
+/**
+ * fdisk_get_size_unit:
+ * @cxt: fdisk context
+ *
+ * Gets unit for SIZE output field (see fdisk_partition_to_string()).
+ *
+ * Returns: unit
+ */
+int fdisk_get_size_unit(struct fdisk_context *cxt)
+{
+       assert(cxt);
+       return cxt->sizeunit;
+}
 
 /**
  * fdisk_get_nsectors:
@@ -945,7 +1380,7 @@ sector_t fdisk_set_last_lba(struct fdisk_context *cxt, sector_t lba)
  *
  * Returns: size of the device in logical sectors.
  */
-sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
 {
        assert(cxt);
        return cxt->total_sectors;
@@ -963,11 +1398,59 @@ const char *fdisk_get_devname(struct fdisk_context *cxt)
        return cxt->dev_path;
 }
 
+/**
+ * fdisk_get_devno:
+ * @cxt: context
+ *
+ * Returns: device number or zero for non-block devices
+ *
+ * Since: 2.33
+ */
+dev_t fdisk_get_devno(struct fdisk_context *cxt)
+{
+       assert(cxt);
+       return S_ISBLK(cxt->dev_st.st_mode) ? cxt->dev_st.st_rdev : 0;
+}
+
+/**
+ * fdisk_get_devmodel:
+ * @cxt: context
+ *
+ * Returns: device model string or NULL.
+ *
+ * Since: 2.33
+ */
+#ifdef __linux__
+const char *fdisk_get_devmodel(struct fdisk_context *cxt)
+{
+       assert(cxt);
+
+       if (cxt->dev_model_probed)
+               return cxt->dev_model;
+
+       if (fdisk_get_devno(cxt)) {
+               struct path_cxt *pc = ul_new_sysfs_path(fdisk_get_devno(cxt), NULL, NULL);
+
+               if (pc) {
+                       ul_path_read_string(pc, &cxt->dev_model, "device/model");
+                       ul_unref_path(pc);
+               }
+       }
+       cxt->dev_model_probed = 1;
+       return cxt->dev_model;
+}
+#else
+const char *fdisk_get_devmodel(struct fdisk_context *cxt __attribute__((__unused__)))
+{
+       return NULL;
+}
+#endif
+
 /**
  * fdisk_get_devfd:
  * @cxt: context
  *
- * Retruns: device file descriptor.
+ * Returns: device file descriptor.
  */
 int fdisk_get_devfd(struct fdisk_context *cxt)
 {
@@ -992,7 +1475,7 @@ unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
  *
  * Returns: number of geometry sectors.
  */
-sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
 {
        assert(cxt);
        return cxt->geom.sectors;
@@ -1005,7 +1488,7 @@ sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
  *
  * Returns: number of geometry cylinders
  */
-sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
 {
        assert(cxt);
        return cxt->geom.cylinders;
@@ -1015,8 +1498,6 @@ int fdisk_missing_geometry(struct fdisk_context *cxt)
 {
        int rc;
 
-       assert(cxt);
-
        if (!cxt || !cxt->label)
                return 0;