]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - libfdisk/src/partition.c
build-sys: remove duplicate includes
[thirdparty/util-linux.git] / libfdisk / src / partition.c
index 8ebdb4682e82a959fc62d179ae1fd9dd5436efe2..9e87a6864682208d1ea8e2948fe25dd9bd8195f8 100644 (file)
@@ -2,6 +2,10 @@
 #include "c.h"
 #include "strutils.h"
 
+#ifdef HAVE_LIBBLKID
+# include <blkid.h>
+#endif
+
 #include "fdiskP.h"
 
 /**
@@ -63,6 +67,11 @@ void fdisk_reset_partition(struct fdisk_partition *pa)
        free(pa->name);
        free(pa->uuid);
        free(pa->attrs);
+       free(pa->fstype);
+       free(pa->fsuuid);
+       free(pa->fslabel);
+       free(pa->start_chs);
+       free(pa->end_chs);
 
        memset(pa, 0, sizeof(*pa));
        pa->refcount = ref;
@@ -70,11 +79,51 @@ void fdisk_reset_partition(struct fdisk_partition *pa)
        init_partition(pa);
 }
 
+static struct fdisk_partition *__copy_partition(struct fdisk_partition *o)
+{
+       struct fdisk_partition *n = fdisk_new_partition();
+       int rc;
+
+       if (!n)
+               return NULL;
+
+       memcpy(n, o, sizeof(*n));
+
+       /* do not copy reference to lists, etc.*/
+       n->refcount = 1;
+       INIT_LIST_HEAD(&n->parts);
+
+       if (n->type)
+               fdisk_ref_parttype(n->type);
+
+       rc = strdup_between_structs(n, o, name);
+       if (!rc)
+               rc = strdup_between_structs(n, o, uuid);
+       if (!rc)
+               rc = strdup_between_structs(n, o, attrs);
+       if (!rc)
+               rc = strdup_between_structs(n, o, fstype);
+       if (!rc)
+               rc = strdup_between_structs(n, o, fsuuid);
+       if (!rc)
+               rc = strdup_between_structs(n, o, fslabel);
+       if (!rc)
+               rc = strdup_between_structs(n, o, start_chs);
+       if (!rc)
+               rc = strdup_between_structs(n, o, end_chs);
+
+       if (rc) {
+               fdisk_unref_partition(n);
+               n = NULL;
+       }
+       return n;
+}
+
 /**
  * fdisk_ref_partition:
  * @pa: partition pointer
  *
- * Incremparts reference counter.
+ * Increments reference counter.
  */
 void fdisk_ref_partition(struct fdisk_partition *pa)
 {
@@ -86,7 +135,7 @@ void fdisk_ref_partition(struct fdisk_partition *pa)
  * fdisk_unref_partition:
  * @pa: partition pointer
  *
- * De-incremparts reference counter, on zero the @pa is automatically
+ * Decrements reference counter, on zero the @pa is automatically
  * deallocated.
  */
 void fdisk_unref_partition(struct fdisk_partition *pa)
@@ -96,8 +145,8 @@ void fdisk_unref_partition(struct fdisk_partition *pa)
 
        pa->refcount--;
        if (pa->refcount <= 0) {
-               fdisk_reset_partition(pa);
                list_del(&pa->parts);
+               fdisk_reset_partition(pa);
                DBG(PART, ul_debugobj(pa, "free"));
                free(pa);
        }
@@ -109,7 +158,7 @@ void fdisk_unref_partition(struct fdisk_partition *pa)
  * @off: offset in sectors, maximal is UINT64_MAX-1
  *
  * Note that zero is valid offset too. Use fdisk_partition_unset_start() to
- * undefine the offset. 
+ * undefine the offset.
  *
  * Returns: 0 on success, <0 on error.
  */
@@ -120,6 +169,7 @@ int fdisk_partition_set_start(struct fdisk_partition *pa, fdisk_sector_t off)
        if (FDISK_IS_UNDEF(off))
                return -ERANGE;
        pa->start = off;
+       pa->fs_probed = 0;
        return 0;
 }
 
@@ -136,6 +186,7 @@ int fdisk_partition_unset_start(struct fdisk_partition *pa)
        if (!pa)
                return -EINVAL;
        FDISK_INIT_UNDEF(pa->start);
+       pa->fs_probed = 0;
        return 0;
 }
 
@@ -172,21 +223,21 @@ int fdisk_partition_has_start(struct fdisk_partition *pa)
  * @a: partition
  * @b: partition
  *
- * Compares partitons according to start offset, See fdisk_sort_table().
+ * Compares partitions according to start offset, See fdisk_table_sort_partitions().
  *
  * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
  */
 int fdisk_partition_cmp_start(struct fdisk_partition *a,
                              struct fdisk_partition *b)
 {
-       int is_a = FDISK_IS_UNDEF(a->start),
-           is_b = FDISK_IS_UNDEF(b->start);
+       int no_a = FDISK_IS_UNDEF(a->start),
+           no_b = FDISK_IS_UNDEF(b->start);
 
-       if (!is_a && !is_b)
+       if (no_a && no_b)
                return 0;
-       if (!is_a)
+       if (no_a)
                return -1;
-       if (!is_b)
+       if (no_b)
                return 1;
 
        return cmp_numbers(a->start, b->start);
@@ -197,7 +248,7 @@ int fdisk_partition_cmp_start(struct fdisk_partition *a,
  * @pa: partition
  * @enable: 0|1
  *
- * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * When @pa used as a template for fdisk_add_partition() when force label driver
  * to use the first possible space for the new partition.
  *
  * Returns: 0 on success, <0 on error.
@@ -224,7 +275,6 @@ int fdisk_partition_start_is_default(struct fdisk_partition *pa)
        return pa->start_follow_default;
 }
 
-
 /**
  * fdisk_partition_set_size:
  * @pa: partition
@@ -242,6 +292,7 @@ int fdisk_partition_set_size(struct fdisk_partition *pa, fdisk_sector_t sz)
        if (FDISK_IS_UNDEF(sz))
                return -ERANGE;
        pa->size = sz;
+       pa->fs_probed = 0;
        return 0;
 }
 
@@ -258,6 +309,7 @@ int fdisk_partition_unset_size(struct fdisk_partition *pa)
        if (!pa)
                return -EINVAL;
        FDISK_INIT_UNDEF(pa->size);
+       pa->fs_probed = 0;
        return 0;
 }
 
@@ -267,7 +319,7 @@ int fdisk_partition_unset_size(struct fdisk_partition *pa)
  *
  * The zero is also valid size. The function may return random undefined
  * value when size is undefined (for example after fdisk_partition_unset_size()).
- * Always use fdisk_partition_has_size() to be sure that you work with valid 
+ * Always use fdisk_partition_has_size() to be sure that you work with valid
  * numbers.
  *
  * Returns: size offset in sectors
@@ -294,7 +346,7 @@ int fdisk_partition_has_size(struct fdisk_partition *pa)
  * @enable: 0|1
  *
  * By default libfdisk aligns the size when add the new partition (by
- * fdisk_add_partrition()). If you want to disable this functionality use
+ * fdisk_add_partition()). If you want to disable this functionality use
  * @enable = 1.
  *
  * Returns: 0 on success, <0 on error.
@@ -310,7 +362,7 @@ int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable)
 /**
  * fdisk_partition_set_partno:
  * @pa: partition
- * @num: partitin number (0 is the first partition, maximal is SIZE_MAX-1)
+ * @num: partition number (0 is the first partition, maximal is SIZE_MAX-1)
  *
  * Note that zero is valid partno too. Use fdisk_partition_unset_partno() to
  * undefine the partno.
@@ -347,7 +399,7 @@ int fdisk_partition_unset_partno(struct fdisk_partition *pa)
  * fdisk_partition_get_partno:
  * @pa: partition
  *
- * The zero is also valid parition number. The function may return random
+ * The zero is also valid partition number. The function may return random
  * value when partno is undefined (for example after fdisk_partition_unset_partno()).
  * Always use fdisk_partition_has_partno() to be sure that you work with valid
  * numbers.
@@ -376,7 +428,7 @@ int fdisk_partition_has_partno(struct fdisk_partition *pa)
  * @a: partition
  * @b: partition
  *
- * Compares partitons according to partition number See fdisk_sort_table().
+ * Compares partitions according to partition number See fdisk_table_sort_partitions().
  *
  * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
  */
@@ -391,7 +443,7 @@ int fdisk_partition_cmp_partno(struct fdisk_partition *a,
  * @pa: partition
  * @enable: 0|1
  *
- * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * When @pa used as a template for fdisk_add_partition() when force label driver
  * to add a new partition to the default (next) position.
  *
  * Returns: 0 on success, <0 on error.
@@ -409,7 +461,7 @@ int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable
  * @pa: partition
  * @type: partition type
  *
- * Sets parition type.
+ * Sets partition type.
  *
  * Returns: 0 on success, <0 on error.
  */
@@ -439,18 +491,9 @@ struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa)
 
 int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name)
 {
-       char *p = NULL;
-
        if (!pa)
                return -EINVAL;
-       if (name) {
-              p = strdup(name);
-              if (!p)
-                      return -ENOMEM;
-       }
-       free(pa->name);
-       pa->name = p;
-       return 0;
+       return strdup_to_struct_member(pa, name, name);
 }
 
 const char *fdisk_partition_get_name(struct fdisk_partition *pa)
@@ -460,18 +503,9 @@ const char *fdisk_partition_get_name(struct fdisk_partition *pa)
 
 int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid)
 {
-       char *p = NULL;
-
        if (!pa)
                return -EINVAL;
-       if (uuid) {
-              p = strdup(uuid);
-              if (!p)
-                      return -ENOMEM;
-       }
-       free(pa->uuid);
-       pa->uuid = p;
-       return 0;
+       return strdup_to_struct_member(pa, uuid, uuid);
 }
 
 /**
@@ -507,8 +541,8 @@ fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa)
  * @pa: partition
  * @enable: 0|1
  *
- * When @pa used as a tempalate for fdisk_add_partition() when force label driver
- * to use all the possible space for the new partition.
+ * When @pa used as a template for fdisk_add_partition() when force label
+ * driver to use all the possible space for the new partition.
  *
  * Returns: 0 on success, <0 on error.
  */
@@ -532,42 +566,73 @@ int fdisk_partition_end_is_default(struct fdisk_partition *pa)
        return pa->end_follow_default;
 }
 
+/**
+ * fdisk_partition_get_uuid:
+ * @pa: partition
+ *
+ * Returns: partition UUID as string
+ */
 const char *fdisk_partition_get_uuid(struct fdisk_partition *pa)
 {
        return pa ? pa->uuid : NULL;
 }
 
+/**
+ * fdisk_partition_get_attrs:
+ * @pa: partition
+ *
+ * Returns: partition attributes in string format
+ */
 const char *fdisk_partition_get_attrs(struct fdisk_partition *pa)
 {
        return pa ? pa->attrs : NULL;
 }
 
+/**
+ * fdisk_partition_set_attrs:
+ * @pa: partition
+ * @attrs: attributes
+ *
+ * Sets @attrs to @pa.
+ *
+ * Return: 0 on success, <0 on error.
+ */
 int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs)
 {
-       char *p = NULL;
-
        if (!pa)
                return -EINVAL;
-       if (attrs) {
-              p = strdup(attrs);
-              if (!p)
-                      return -ENOMEM;
-       }
-       free(pa->attrs);
-       pa->attrs = p;
-       return 0;
+       return strdup_to_struct_member(pa, attrs, attrs);
 }
 
+/**
+ * fdisk_partition_is_nested:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition is nested (e.g. MBR logical partition)
+ */
 int fdisk_partition_is_nested(struct fdisk_partition *pa)
 {
        return pa && !FDISK_IS_UNDEF(pa->parent_partno);
 }
 
+/**
+ * fdisk_partition_is_container:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition is container (e.g. MBR extended partition)
+ */
 int fdisk_partition_is_container(struct fdisk_partition *pa)
 {
        return pa && pa->container;
 }
 
+/**
+ * fdisk_partition_get_parent:
+ * @pa: partition
+ * @parent: parent parno
+ *
+ * Returns: returns devno of the parent
+ */
 int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
 {
        if (pa && parent)
@@ -577,28 +642,72 @@ int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
        return 0;
 }
 
+/**
+ * fdisk_partition_is_used:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition points to some area
+ */
 int fdisk_partition_is_used(struct fdisk_partition *pa)
 {
        return pa && pa->used;
 }
 
+/**
+ * fdisk_partition_is_bootable:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition has enabled boot flag
+ */
 int fdisk_partition_is_bootable(struct fdisk_partition *pa)
 {
        return pa && pa->boot == 1;
 }
 
+/**
+ * fdisk_partition_is_freespace:
+ * @pa: partition
+ *
+ * Returns: 1 if @pa points to freespace
+ */
 int fdisk_partition_is_freespace(struct fdisk_partition *pa)
 {
        return pa && pa->freespace;
 }
 
+/**
+ * fdisk_partition_is_wholedisk:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition is special whole-disk (e.g. SUN) partition
+ */
+int fdisk_partition_is_wholedisk(struct fdisk_partition *pa)
+{
+       return pa && pa->wholedisk;
+}
+
+/**
+ * fdisk_partition_next_partno:
+ * @pa: partition
+ * @cxt: context
+ * @n: returns partition number
+ *
+ * If @pa specified and partno-follow-default (see fdisk_partition_partno_follow_default())
+ * enabled then returns next expected partno or -ERANGE on error.
+ *
+ * If @pa is NULL, or @pa does not specify any semantic for the next partno
+ * then use Ask API to ask user for the next partno. In this case returns 1 if
+ * no free partition available. If fdisk dialogs are disabled then returns -EINVAL.
+ *
+ * Returns: 0 on success, <0 on error, or 1 for non-free partno by Ask API.
+ */
 int fdisk_partition_next_partno(
                struct fdisk_partition *pa,
                struct fdisk_context *cxt,
                size_t *n)
 {
-       assert(cxt);
-       assert(n);
+       if (!cxt || !n)
+               return -EINVAL;
 
        if (pa && pa->partno_follow_default) {
                size_t i;
@@ -617,13 +726,70 @@ int fdisk_partition_next_partno(
 
                DBG(PART, ul_debugobj(pa, "next partno (specified=%zu)", pa->partno));
 
-               if (pa->partno >= cxt->label->nparts_max)
+               if (pa->partno >= cxt->label->nparts_max ||
+                   fdisk_is_partition_used(cxt, pa->partno))
                        return -ERANGE;
                *n = pa->partno;
-       } else
+               return 0;
+
+       } else if (fdisk_has_dialogs(cxt))
                return fdisk_ask_partnum(cxt, n, 1);
 
-       return 0;
+       return -EINVAL;
+}
+
+static int probe_partition_content(struct fdisk_context *cxt, struct fdisk_partition *pa)
+{
+       int rc = 1;     /* nothing */
+
+       DBG(PART, ul_debugobj(pa, "start probe #%zu partition [cxt %p] >>>", pa->partno, cxt));
+
+       /* zeroize the current setting */
+       strdup_to_struct_member(pa, fstype, NULL);
+       strdup_to_struct_member(pa, fsuuid, NULL);
+       strdup_to_struct_member(pa, fslabel, NULL);
+
+       if (!fdisk_partition_has_start(pa) ||
+           !fdisk_partition_has_size(pa))
+               goto done;
+
+#ifdef HAVE_LIBBLKID
+       else {
+               uintmax_t start, size;
+
+               blkid_probe pr = blkid_new_probe();
+               if (!pr)
+                       goto done;
+
+               DBG(PART, ul_debugobj(pa, "blkid prober: %p", pr));
+
+               start = fdisk_partition_get_start(pa) * fdisk_get_sector_size(cxt);
+               size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cxt);
+
+               if (blkid_probe_set_device(pr, cxt->dev_fd, start, size) == 0
+                   && blkid_do_fullprobe(pr) == 0) {
+
+                       const char *data;
+                       rc = 0;
+
+                       if (!blkid_probe_lookup_value(pr, "TYPE",  &data, NULL))
+                               rc = strdup_to_struct_member(pa, fstype, data);
+
+                       if (!rc && !blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
+                               rc = strdup_to_struct_member(pa, fslabel, data);
+
+                       if (!rc && !blkid_probe_lookup_value(pr, "UUID",  &data, NULL))
+                               rc = strdup_to_struct_member(pa, fsuuid, data);
+               }
+
+               blkid_free_probe(pr);
+               pa->fs_probed = 1;
+       }
+#endif /* HAVE_LIBBLKID */
+
+done:
+       DBG(PART, ul_debugobj(pa, "<<< end probe #%zu partition[cxt %p, rc=%d]", pa->partno, cxt, rc));
+       return rc;
 }
 
 /**
@@ -638,7 +804,7 @@ int fdisk_partition_next_partno(
  * For example
  * <informalexample>
  *   <programlisting>
- *      struct fdisk_parition *pa;
+ *      struct fdisk_partition *pa;
  *
  *      fdisk_get_partition(cxt, 0, &pa);
  *     fdisk_partition_to_string(pa, FDISK_FIELD_UUID, &data);
@@ -661,7 +827,7 @@ int fdisk_partition_to_string(struct fdisk_partition *pa,
        int rc = 0;
        uint64_t x;
 
-       if (!pa || !cxt)
+       if (!pa || !cxt || !data)
                return -EINVAL;
 
        switch (id) {
@@ -676,59 +842,69 @@ int fdisk_partition_to_string(struct fdisk_partition *pa,
                }
                break;
        case FDISK_FIELD_BOOT:
-               if (fdisk_partition_is_bootable(pa))
-                       rc = asprintf(&p, "%c", pa->boot ? '*' : ' ');
+               p = fdisk_partition_is_bootable(pa) ? strdup("*") : NULL;
                break;
        case FDISK_FIELD_START:
                if (fdisk_partition_has_start(pa)) {
                        x = fdisk_cround(cxt, pa->start);
                        rc = pa->start_post ?
-                               asprintf(&p, "%ju%c", x, pa->start_post) :
-                               asprintf(&p, "%ju", x);
+                               asprintf(&p, "%"PRIu64"%c", x, pa->start_post) :
+                               asprintf(&p, "%"PRIu64, x);
                }
                break;
        case FDISK_FIELD_END:
                if (fdisk_partition_has_end(pa)) {
                        x = fdisk_cround(cxt, fdisk_partition_get_end(pa));
                        rc = pa->end_post ?
-                                       asprintf(&p, "%ju%c", x, pa->end_post) :
-                                       asprintf(&p, "%ju", x);
+                                       asprintf(&p, "%"PRIu64"%c", x, pa->end_post) :
+                                       asprintf(&p, "%"PRIu64, x);
                }
                break;
        case FDISK_FIELD_SIZE:
                if (fdisk_partition_has_size(pa)) {
                        uint64_t sz = pa->size * cxt->sector_size;
 
-                       if (fdisk_is_details(cxt)) {
-                               rc = pa->size_post ?
-                                               asprintf(&p, "%ju%c", sz, pa->size_post) :
-                                               asprintf(&p, "%ju", sz);
-                       } else {
-                               p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
-                               if (!p)
-                                       rc = -ENOMEM;
+                       switch (cxt->sizeunit) {
+                       case FDISK_SIZEUNIT_BYTES:
+                               rc = asprintf(&p, "%"PRIu64"", sz);
+                               break;
+                       case FDISK_SIZEUNIT_HUMAN:
+                               if (fdisk_is_details(cxt))
+                                       rc = pa->size_post ?
+                                                       asprintf(&p, "%"PRIu64"%c", sz, pa->size_post) :
+                                                       asprintf(&p, "%"PRIu64, sz);
+                               else {
+                                       p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
+                                       if (!p)
+                                               rc = -ENOMEM;
+                               }
+                               break;
                        }
                }
                break;
        case FDISK_FIELD_CYLINDERS:
-               rc = asprintf(&p, "%ju", (uintmax_t)
-                       fdisk_cround(cxt, fdisk_partition_has_size(pa) ? pa->size : 0));
+       {
+               uintmax_t sz = fdisk_partition_has_size(pa) ? pa->size : 0;
+               if (sz)
+                       /* Why we need to cast that to uintmax_t? */
+                       rc = asprintf(&p, "%ju", (uintmax_t)(sz / (cxt->geom.heads * cxt->geom.sectors)) + 1);
                break;
+       }
        case FDISK_FIELD_SECTORS:
                rc = asprintf(&p, "%ju",
                        fdisk_partition_has_size(pa) ? (uintmax_t) pa->size : 0);
                break;
        case FDISK_FIELD_BSIZE:
-               rc = asprintf(&p, "%ju", pa->bsize);
+               rc = asprintf(&p, "%"PRIu64, pa->bsize);
                break;
        case FDISK_FIELD_FSIZE:
-               rc = asprintf(&p, "%ju", pa->fsize);
+               rc = asprintf(&p, "%"PRIu64, pa->fsize);
                break;
        case FDISK_FIELD_CPG:
-               rc = asprintf(&p, "%ju", pa->cpg);
+               rc = asprintf(&p, "%"PRIu64, pa->cpg);
                break;
        case FDISK_FIELD_TYPE:
-               p = pa->type && pa->type->name ? strdup(pa->type->name) : NULL;
+               p = pa->type && pa->type->name ? strdup(_(pa->type->name)) : NULL;
                break;
        case FDISK_FIELD_TYPEID:
                if (pa->type && fdisk_parttype_get_string(pa->type))
@@ -737,34 +913,50 @@ int fdisk_partition_to_string(struct fdisk_partition *pa,
                        rc = asprintf(&p, "%x", fdisk_parttype_get_code(pa->type));
                break;
        case FDISK_FIELD_UUID:
-               p = pa->uuid ? strdup(pa->uuid) : NULL;
+               p = pa->uuid && *pa->uuid? strdup(pa->uuid) : NULL;
                break;
        case FDISK_FIELD_NAME:
-               p = pa->name ? strdup(pa->name) : NULL;
+               p = pa->name && *pa->name ? strdup(pa->name) : NULL;
                break;
        case FDISK_FIELD_ATTR:
-               p = pa->attrs ? strdup(pa->attrs) : NULL;
+               p = pa->attrs && *pa->attrs ? strdup(pa->attrs) : NULL;
                break;
        case FDISK_FIELD_SADDR:
-               p = pa->start_chs ? strdup(pa->start_chs) : NULL;
+               p = pa->start_chs && *pa->start_chs ? strdup(pa->start_chs) : NULL;
                break;
        case FDISK_FIELD_EADDR:
-               p = pa->end_chs ? strdup(pa->end_chs) : NULL;
+               p = pa->end_chs && *pa->end_chs? strdup(pa->end_chs) : NULL;
+               break;
+       case FDISK_FIELD_FSUUID:
+               if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
+                       p = pa->fsuuid && *pa->fsuuid ? strdup(pa->fsuuid) : NULL;
+               break;
+       case FDISK_FIELD_FSLABEL:
+               if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
+                       p = pa->fslabel && *pa->fslabel ? strdup(pa->fslabel) : NULL;
+               break;
+       case FDISK_FIELD_FSTYPE:
+               if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
+                       p = pa->fstype && *pa->fstype ? strdup(pa->fstype) : NULL;
                break;
        default:
                return -EINVAL;
        }
 
-       if (rc < 0)
+       if (rc < 0) {
                rc = -ENOMEM;
-       else if (rc > 0)
+               free(p);
+               p = NULL;
+
+       } else if (rc > 0)
                rc = 0;
 
-       if (data)
-               *data = p;
+       *data = p;
+
        return rc;
 }
 
+
 /**
  * fdisk_get_partition:
  * @cxt: context
@@ -814,6 +1006,263 @@ int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
        return rc;
 }
 
+static struct fdisk_partition *resize_get_by_offset(
+                       struct fdisk_table *tb,
+                       struct fdisk_partition *cur,
+                       fdisk_sector_t off)
+{
+       struct fdisk_partition *pa = NULL;
+       struct fdisk_iter itr;
+
+       fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+       while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+               if (!fdisk_partition_has_start(pa) || !fdisk_partition_has_size(pa))
+                       continue;
+               if (fdisk_partition_is_nested(cur) &&
+                   pa->parent_partno != cur->parent_partno)
+                       continue;
+               if (off >= pa->start && off < pa->start + pa->size)
+                       return pa;
+       }
+
+       return NULL;
+}
+
+/*
+ * Verify that area addressed by @start is freespace or the @cur[rent]
+ * partition and continue to the next table entries until it's freespace, and
+ * counts size of all this space.
+ *
+ * This is core of the partition start offset move operation. We can move the
+ * start within the current partition of to the another free space. It's
+ * forbidden to move start of the partition to another already defined
+ * partition.
+ */
+static int resize_get_last_possible(
+                       struct fdisk_table *tb,
+                       struct fdisk_partition *cur,
+                       fdisk_sector_t start,
+                       fdisk_sector_t *maxsz)
+{
+       struct fdisk_partition *pa = NULL, *last = NULL;
+       struct fdisk_iter itr;
+
+       fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+       *maxsz = 0;
+       DBG(TAB, ul_debugobj(tb, "checking last possible for start=%ju", (uintmax_t) start));
+
+
+       while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+
+               DBG(TAB, ul_debugobj(tb, " checking entry %p [partno=%zu start=%ju, end=%ju, size=%ju%s%s%s]",
+                       pa,
+                       fdisk_partition_get_partno(pa),
+                       (uintmax_t) fdisk_partition_get_start(pa),
+                       (uintmax_t) fdisk_partition_get_end(pa),
+                       (uintmax_t) fdisk_partition_get_size(pa),
+                       fdisk_partition_is_freespace(pa) ? " freespace" : "",
+                       fdisk_partition_is_nested(pa)    ? " nested"    : "",
+                       fdisk_partition_is_container(pa) ? " container" : ""));
+
+               if (!fdisk_partition_has_start(pa) ||
+                   !fdisk_partition_has_size(pa) ||
+                   (fdisk_partition_is_container(pa) && pa != cur)) {
+                       DBG(TAB, ul_debugobj(tb, "  ignored (no start/size or container)"));
+                       continue;
+               }
+
+               if (fdisk_partition_is_nested(pa)
+                   && fdisk_partition_is_container(cur)
+                   && pa->parent_partno == cur->partno) {
+                       DBG(TAB, ul_debugobj(tb, "  ignore (nested child of the current partition)"));
+                       continue;
+               }
+
+               /* The current is nested, free space has to be nested within the same parent */
+               if (fdisk_partition_is_nested(cur)
+                   && pa->parent_partno != cur->parent_partno) {
+                       DBG(TAB, ul_debugobj(tb, "  ignore (nested required)"));
+                       continue;
+               }
+
+               if (!last) {
+                       if (start >= pa->start &&  start < pa->start + pa->size) {
+                               if (fdisk_partition_is_freespace(pa) || pa == cur) {
+                                       DBG(TAB, ul_debugobj(tb, "  accepted as last"));
+                                       last = pa;
+                               } else {
+                                       DBG(TAB, ul_debugobj(tb, "  failed to set last"));
+                                       break;
+                               }
+
+
+                               *maxsz = pa->size - (start - pa->start);
+                               DBG(TAB, ul_debugobj(tb, "  new max=%ju", (uintmax_t) *maxsz));
+                       }
+               } else if (!fdisk_partition_is_freespace(pa) && pa != cur) {
+                       DBG(TAB, ul_debugobj(tb, "  no free space behind current"));
+                       break;
+               } else {
+                       last = pa;
+                       *maxsz = pa->size - (start - pa->start);
+                       DBG(TAB, ul_debugobj(tb, "  new max=%ju (last updated)", (uintmax_t) *maxsz));
+               }
+       }
+
+       if (last)
+               DBG(PART, ul_debugobj(cur, "resize: max size=%ju", (uintmax_t) *maxsz));
+       else
+               DBG(PART, ul_debugobj(cur, "resize: nothing usable after %ju", (uintmax_t) start));
+
+       return last ? 0 : -1;
+}
+
+/*
+ * Uses template @tpl to recount start and size change of the partition @res. The
+ * @tpl->size and @tpl->start are interpreted as relative to the current setting.
+ */
+static int recount_resize(
+                       struct fdisk_context *cxt, size_t partno,
+                       struct fdisk_partition *res, struct fdisk_partition *tpl)
+{
+       fdisk_sector_t start, size, xsize;
+       struct fdisk_partition *cur = NULL;
+       struct fdisk_table *tb = NULL;
+       int rc;
+
+       DBG(PART, ul_debugobj(tpl, ">>> resize requested"));
+
+       FDISK_INIT_UNDEF(start);
+       FDISK_INIT_UNDEF(size);
+
+       rc = fdisk_get_partitions(cxt, &tb);
+       if (!rc) {
+               /* For resize we do not follow grain to detect free-space, but
+                * we allow to resize with very small granulation. */
+               unsigned long org = cxt->grain;
+
+               cxt->grain = cxt->sector_size;
+               rc = fdisk_get_freespaces(cxt, &tb);
+               cxt->grain = org;
+       }
+       if (rc) {
+               fdisk_unref_table(tb);
+               return rc;
+       }
+
+       fdisk_table_sort_partitions(tb, fdisk_partition_cmp_start);
+
+       DBG(PART, ul_debugobj(tpl, "resize partition partno=%zu in table:", partno));
+       ON_DBG(PART, fdisk_debug_print_table(tb));
+
+       cur = fdisk_table_get_partition_by_partno(tb, partno);
+       if (!cur) {
+               fdisk_unref_table(tb);
+               return -EINVAL;
+       }
+
+       /* 1a) set new start - change relative to the current on-disk setting */
+       if (tpl->movestart && fdisk_partition_has_start(tpl)) {
+               start = fdisk_partition_get_start(cur);
+               if (tpl->movestart == FDISK_MOVE_DOWN) {
+                       if (fdisk_partition_get_start(tpl) > start)
+                               goto erange;
+                       start -= fdisk_partition_get_start(tpl);
+               } else
+                       start += fdisk_partition_get_start(tpl);
+
+               DBG(PART, ul_debugobj(tpl, "resize: moving start %s relative, new start: %ju",
+                               tpl->movestart == FDISK_MOVE_DOWN  ? "DOWN" : "UP", (uintmax_t)start));
+
+       /* 1b) set new start - absolute number */
+       } else if (fdisk_partition_has_start(tpl)) {
+               start = fdisk_partition_get_start(tpl);
+               DBG(PART, ul_debugobj(tpl, "resize: moving start to absolute offset: %ju",
+                                     (uintmax_t)start));
+       }
+
+       /* 2) verify that start is within the current partition or any freespace area */
+       if (!FDISK_IS_UNDEF(start)) {
+               struct fdisk_partition *area = resize_get_by_offset(tb, cur, start);
+
+               if (area == cur)
+                       DBG(PART, ul_debugobj(tpl, "resize: start points to the current partition"));
+               else if (area && fdisk_partition_is_freespace(area))
+                       DBG(PART, ul_debugobj(tpl, "resize: start points to freespace"));
+               else if (!area && start >= cxt->first_lba && start < cxt->first_lba + (cxt->grain / cxt->sector_size))
+                       DBG(PART, ul_debugobj(tpl, "resize: start points before first partition"));
+               else {
+                       DBG(PART, ul_debugobj(tpl, "resize: start verification failed"));
+                       goto erange;
+               }
+       } else {
+               /* no change, start points to the current partition */
+               DBG(PART, ul_debugobj(tpl, "resize: start unchanged"));
+               start = fdisk_partition_get_start(cur);
+       }
+
+       /* 3a) set new size -- reduce */
+       if (tpl->resize == FDISK_RESIZE_REDUCE && fdisk_partition_has_size(tpl)) {
+               DBG(PART, ul_debugobj(tpl, "resize: reduce"));
+               size = fdisk_partition_get_size(cur);
+               if (fdisk_partition_get_size(tpl) > size)
+                       goto erange;
+               size -= fdisk_partition_get_size(tpl);
+
+       /* 3b) set new size -- enlarge */
+       } else if (tpl->resize == FDISK_RESIZE_ENLARGE && fdisk_partition_has_size(tpl)) {
+               DBG(PART, ul_debugobj(tpl, "resize: enlarge"));
+               size = fdisk_partition_get_size(cur);
+               size += fdisk_partition_get_size(tpl);
+
+       /* 3c) set new size -- no size specified, enlarge to all freespace */
+       } else if (tpl->resize == FDISK_RESIZE_ENLARGE) {
+               DBG(PART, ul_debugobj(tpl, "resize: enlarge to all possible"));
+               if (resize_get_last_possible(tb, cur, start, &size))
+                       goto erange;
+
+       /* 3d) set new size -- absolute number */
+       } else if (fdisk_partition_has_size(tpl)) {
+               DBG(PART, ul_debugobj(tpl, "resize: new absolute size"));
+               size = fdisk_partition_get_size(tpl);
+       }
+
+       /* 4) verify that size is within the current partition or next free space */
+       xsize = !FDISK_IS_UNDEF(size) ? size : fdisk_partition_get_size(cur);
+
+       if (fdisk_partition_has_size(cur)) {
+               fdisk_sector_t maxsz;
+
+               if (resize_get_last_possible(tb, cur, start, &maxsz))
+                       goto erange;
+               DBG(PART, ul_debugobj(tpl, "resize: size=%ju, max=%ju",
+                                       (uintmax_t) xsize, (uintmax_t) maxsz));
+               if (xsize > maxsz)
+                       goto erange;
+       }
+
+       if (FDISK_IS_UNDEF(size)) {
+               DBG(PART, ul_debugobj(tpl, "resize: size unchanged (undefined)"));
+       }
+
+
+       DBG(PART, ul_debugobj(tpl, "<<< resize: SUCCESS: start %ju->%ju; size %ju->%ju",
+                       (uintmax_t) fdisk_partition_get_start(cur), (uintmax_t) start,
+                       (uintmax_t) fdisk_partition_get_size(cur), (uintmax_t) size));
+       res->start = start;
+       res->size = size;
+       fdisk_unref_table(tb);
+       return 0;
+erange:
+       DBG(PART, ul_debugobj(tpl, "<<< resize: FAILED"));
+       fdisk_warnx(cxt, _("Failed to resize partition #%zu."), partno + 1);
+       fdisk_unref_table(tb);
+       return -ERANGE;
+
+}
+
 /**
  * fdisk_set_partition:
  * @cxt: context
@@ -827,33 +1276,126 @@ int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
 int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
                        struct fdisk_partition *pa)
 {
+       struct fdisk_partition *xpa = pa, *tmp = NULL;
+       int rc, wipe = 0;
+
        if (!cxt || !cxt->label || !pa)
                return -EINVAL;
        if (!cxt->label->op->set_part)
                return -ENOSYS;
 
-       DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju, "
-                   "defaults(start=%s, end=%s, partno=%s)",
-                   partno, pa,
-                   (uintmax_t) fdisk_partition_get_start(pa),
-                   (uintmax_t) fdisk_partition_get_end(pa),
-                   (uintmax_t) fdisk_partition_get_size(pa),
-                   pa->start_follow_default ? "yes" : "no",
-                   pa->end_follow_default ? "yes" : "no",
-                   pa->partno_follow_default ? "yes" : "no"));
+       pa->fs_probed = 0;
 
-       return cxt->label->op->set_part(cxt, partno, pa);
+       if (!fdisk_is_partition_used(cxt, partno)) {
+               pa->partno = partno;
+               return fdisk_add_partition(cxt, pa, NULL);
+       }
+
+       if (pa->resize || fdisk_partition_has_start(pa) || fdisk_partition_has_size(pa)) {
+               xpa = __copy_partition(pa);
+               if (!xpa) {
+                       rc = -ENOMEM;
+                       goto done;
+               }
+               xpa->movestart = 0;
+               xpa->resize = 0;
+               FDISK_INIT_UNDEF(xpa->size);
+               FDISK_INIT_UNDEF(xpa->start);
+
+               rc = recount_resize(cxt, partno, xpa, pa);
+               if (rc)
+                       goto done;
+       }
+
+       DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju)",
+                   partno, xpa,
+                   (uintmax_t) fdisk_partition_get_start(xpa),
+                   (uintmax_t) fdisk_partition_get_end(xpa),
+                   (uintmax_t) fdisk_partition_get_size(xpa)));
+
+       /* disable wipe for old offset/size setting */
+       if (fdisk_get_partition(cxt, partno, &tmp) == 0 && tmp) {
+               wipe = fdisk_set_wipe_area(cxt, fdisk_partition_get_start(tmp),
+                                               fdisk_partition_get_size(tmp), FALSE);
+               fdisk_unref_partition(tmp);
+       }
+
+       /* call label driver */
+       rc = cxt->label->op->set_part(cxt, partno, xpa);
+
+       /* enable wipe for new offset/size */
+       if (!rc && wipe)
+               fdisk_wipe_partition(cxt, partno, TRUE);
+done:
+       DBG(CXT, ul_debugobj(cxt, "set_partition() rc=%d", rc));
+       if (xpa != pa)
+               fdisk_unref_partition(xpa);
+       return rc;
 }
 
+/**
+ * fdisk_wipe_partition:
+ * @cxt: fdisk context
+ * @partno: partition number
+ * @enable: 0 or 1
+ *
+ * Enable/disable filesystems/RAIDs wiping in area defined by partition start and size.
+ *
+ * Returns: <0 in case of error, 0 on success
+ * Since: 2.29
+ */
+int fdisk_wipe_partition(struct fdisk_context *cxt, size_t partno, int enable)
+{
+       struct fdisk_partition *pa = NULL;
+       int rc;
+
+       rc = fdisk_get_partition(cxt, partno, &pa);
+       if (rc)
+               return rc;
+
+       rc = fdisk_set_wipe_area(cxt, fdisk_partition_get_start(pa),
+                                     fdisk_partition_get_size(pa), enable);
+       fdisk_unref_partition(pa);
+       return rc < 0 ? rc : 0;
+}
+
+/**
+ * fdisk_partition_has_wipe:
+ * @cxt: fdisk context
+ * @pa: partition
+ *
+ * Since: 2.30
+ *
+ * Returns: 1 if the area specified by @pa will be wiped by write command, or 0.
+ */
+int fdisk_partition_has_wipe(struct fdisk_context *cxt, struct fdisk_partition *pa)
+{
+       return fdisk_has_wipe_area(cxt, fdisk_partition_get_start(pa),
+                                       fdisk_partition_get_size(pa));
+}
+
+
 /**
  * fdisk_add_partition:
  * @cxt: fdisk context
  * @pa: template for the partition (or NULL)
  * @partno: NULL or returns new partition number
  *
- * If @pa is not specified or any @pa item is missiong the libfdisk will ask by
+ * If @pa is not specified or any @pa item is missing the libfdisk will ask by
  * fdisk_ask_ API.
  *
+ * The @pa template is is important for non-interactive partitioning,
+ * especially for MBR where is necessary to differentiate between
+ * primary/logical; this is done by start offset or/and partno.
+ * The rules for MBR:
+ *
+ *   A) template specifies start within extended partition: add logical
+ *   B) template specifies start out of extended partition: add primary
+ *   C) template specifies start (or default), partno < 4: add primary
+ *   D) template specifies default start, partno >= 4: add logical
+ *
+ * otherwise MBR driver uses Ask-API to get missing information.
+ *
  * Adds a new partition to disklabel.
  *
  * Returns: 0 on success, <0 on error.
@@ -864,9 +1406,6 @@ int fdisk_add_partition(struct fdisk_context *cxt,
 {
        int rc;
 
-       assert(cxt);
-       assert(cxt->label);
-
        if (!cxt || !cxt->label)
                return -EINVAL;
        if (!cxt->label->op->add_part)
@@ -874,17 +1413,21 @@ int fdisk_add_partition(struct fdisk_context *cxt,
        if (fdisk_missing_geometry(cxt))
                return -EINVAL;
 
-       if (pa)
-               DBG(CXT, ul_debugobj(cxt, "adding new partition %p (start=%ju, end=%ju, size=%ju, "
-                           "defaults(start=%s, end=%s, partno=%s)",
-                           pa,
-                           (uintmax_t) fdisk_partition_get_start(pa),
-                           (uintmax_t) fdisk_partition_get_end(pa),
-                           (uintmax_t) fdisk_partition_get_size(pa),
+       if (pa) {
+               pa->fs_probed = 0;
+               DBG(CXT, ul_debugobj(cxt, "adding new partition %p", pa));
+               if (fdisk_partition_has_start(pa))
+                       DBG(CXT, ul_debugobj(cxt, "     start: %ju", (uintmax_t) fdisk_partition_get_start(pa)));
+               if (fdisk_partition_has_end(pa))
+                       DBG(CXT, ul_debugobj(cxt, "       end: %ju", (uintmax_t) fdisk_partition_get_end(pa)));
+               if (fdisk_partition_has_size(pa))
+                       DBG(CXT, ul_debugobj(cxt, "      size: %ju", (uintmax_t) fdisk_partition_get_size(pa)));
+
+               DBG(CXT, ul_debugobj(cxt,         "  defaults: start=%s, end=%s, partno=%s",
                            pa->start_follow_default ? "yes" : "no",
                            pa->end_follow_default ? "yes" : "no",
                            pa->partno_follow_default ? "yes" : "no"));
-       else
+       else
                DBG(CXT, ul_debugobj(cxt, "adding partition"));
 
        rc = cxt->label->op->add_part(cxt, pa, partno);
@@ -909,6 +1452,8 @@ int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
        if (!cxt->label->op->del_part)
                return -ENOSYS;
 
+       fdisk_wipe_partition(cxt, partno, 0);
+
        DBG(CXT, ul_debugobj(cxt, "deleting %s partition number %zd",
                                cxt->label->name, partno));
        return cxt->label->op->del_part(cxt, partno);
@@ -925,7 +1470,7 @@ int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
 int fdisk_delete_all_partitions(struct fdisk_context *cxt)
 {
        size_t i;
-       int rc;
+       int rc = 0;
 
        if (!cxt || !cxt->label)
                return -EINVAL;
@@ -947,6 +1492,9 @@ int fdisk_delete_all_partitions(struct fdisk_context *cxt)
  * @cxt: context
  * @n: partition number (0 is the first partition)
  *
+ * Check if the partition number @n is used by partition table. This function
+ * does not check if the device is used (e.g. mounted) by system!
+ *
  * This is faster than fdisk_get_partition() + fdisk_partition_is_used().
  *
  * Returns: 0 or 1