]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - libfdisk/src/gpt.c
libfdisk: make it possible to use zero for size and start
[thirdparty/util-linux.git] / libfdisk / src / gpt.c
index bfdcd7adc1e75a0edc52966cda7e738910a76aae..ec6dbd6644099a6a7898a0a24f7b22a4ea40cb80 100644 (file)
@@ -99,16 +99,16 @@ struct gpt_header {
        uint32_t            size; /* in bytes */
        uint32_t            crc32; /* header CRC checksum */
        uint32_t            reserved1; /* must be 0 */
-       uint64_t            my_lba; /* LBA that contains this struct (LBA 1) */
+       uint64_t            my_lba; /* LBA of block that contains this struct (LBA 1) */
        uint64_t            alternative_lba; /* backup GPT header */
        uint64_t            first_usable_lba; /* first usable logical block for partitions */
        uint64_t            last_usable_lba; /* last usable logical block for partitions */
        struct gpt_guid     disk_guid; /* unique disk identifier */
-       uint64_t            partition_entry_lba; /* stat LBA of the partition entry array */
+       uint64_t            partition_entry_lba; /* LBA of start of partition entries array */
        uint32_t            npartition_entries; /* total partition entries - normally 128 */
        uint32_t            sizeof_partition_entry; /* bytes for each GUID pt */
        uint32_t            partition_entry_array_crc32; /* partition CRC checksum */
-       uint8_t             reserved2[512 - 92]; /* must be 0 */
+       uint8_t             reserved2[512 - 92]; /* must all be 0 */
 } __attribute__ ((packed));
 
 struct gpt_record {
@@ -325,6 +325,39 @@ static struct fdisk_parttype *gpt_partition_parttype(
        return t ? : fdisk_new_unknown_parttype(0, str);
 }
 
+static void gpt_entry_set_type(struct gpt_entry *e, struct gpt_guid *uuid)
+{
+       e->type = *uuid;
+       DBG(LABEL, gpt_debug_uuid("new type", &(e->type)));
+}
+
+static void gpt_entry_set_name(struct gpt_entry *e, char *str)
+{
+       char name[GPT_PART_NAME_LEN] = { 0 };
+       size_t i, sz = strlen(str);
+
+       if (sz) {
+               if (sz > GPT_PART_NAME_LEN)
+                       sz = GPT_PART_NAME_LEN;
+               memcpy(name, str, sz);
+       }
+
+       for (i = 0; i < GPT_PART_NAME_LEN; i++)
+               e->name[i] = cpu_to_le16((uint16_t) name[i]);
+}
+
+static int gpt_entry_set_uuid(struct gpt_entry *e, char *str)
+{
+       struct gpt_guid uuid;
+       int rc;
+
+       rc = string_to_guid(str, &uuid);
+       if (rc)
+               return rc;
+
+       e->partition_guid = uuid;
+       return 0;
+}
 
 
 static const char *gpt_get_header_revstr(struct gpt_header *header)
@@ -505,6 +538,7 @@ static int gpt_mknew_header(struct fdisk_context *cxt,
                            struct gpt_header *header, uint64_t lba)
 {
        uint64_t first, last;
+       int has_id = 0;
 
        if (!cxt || !header)
                return -ENOSYS;
@@ -525,9 +559,17 @@ static int gpt_mknew_header(struct fdisk_context *cxt,
        header->last_usable_lba  = cpu_to_le64(last);
 
        gpt_mknew_header_common(cxt, header, lba);
-       uuid_generate_random((unsigned char *) &header->disk_guid);
-       swap_efi_guid(&header->disk_guid);
 
+       if (cxt->script) {
+               const char *id = fdisk_script_get_header(cxt->script, "label-id");
+               if (id && string_to_guid(id, &header->disk_guid) == 0)
+                       has_id = 1;
+       }
+
+       if (!has_id) {
+               uuid_generate_random((unsigned char *) &header->disk_guid);
+               swap_efi_guid(&header->disk_guid);
+       }
        return 0;
 }
 
@@ -1328,6 +1370,75 @@ static int gpt_entry_attrs_to_string(struct gpt_entry *e, char **res)
        return 0;
 }
 
+static int gpt_entry_attrs_from_string(
+                       struct fdisk_context *cxt,
+                       struct gpt_entry *e,
+                       const char *str)
+{
+       const char *p = str;
+       uint64_t attrs = 0;
+       char *bits;
+
+       assert(e);
+       assert(p);
+
+       DBG(LABEL, ul_debug("GPT: parsing string attributes '%s'", p));
+
+       bits = (char *) &attrs;
+
+       while (p && *p) {
+               int bit = -1;
+
+               while (isblank(*p)) p++;
+               if (!*p)
+                       break;
+
+               DBG(LABEL, ul_debug(" parsing item '%s'", p));
+
+               if (strncmp(p, "GUID:", 5) == 0) {
+                       p += 5;
+                       continue;
+               } else if (strncmp(p, GPT_ATTRSTR_REQ,
+                                       sizeof(GPT_ATTRSTR_REQ) - 1) == 0) {
+                       bit = GPT_ATTRBIT_REQ;
+                       p += sizeof(GPT_ATTRSTR_REQ) - 1;
+               } else if (strncmp(p, GPT_ATTRSTR_LEGACY,
+                                       sizeof(GPT_ATTRSTR_LEGACY) - 1) == 0) {
+                       bit = GPT_ATTRBIT_LEGACY;
+                       p += sizeof(GPT_ATTRSTR_LEGACY) - 1;
+               } else if (strncmp(p, GPT_ATTRSTR_NOBLOCK,
+                                       sizeof(GPT_ATTRSTR_NOBLOCK) - 1) == 0) {
+                       bit = GPT_ATTRBIT_NOBLOCK;
+                       p += sizeof(GPT_ATTRSTR_NOBLOCK) - 1;
+               } else if (isdigit((unsigned int) *p)) {
+                       char *end = NULL;
+
+                       errno = 0;
+                       bit = strtol(p, &end, 0);
+                       if (errno || !end || end == str
+                           || bit < GPT_ATTRBIT_GUID_FIRST
+                           || bit >= GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT)
+                               bit = -1;
+                       else
+                               p = end;
+               }
+
+               if (bit < 0) {
+                       fdisk_warnx(cxt, _("unssuported GPT attribute bit '%s'"), p);
+                       return -EINVAL;
+               }
+
+               setbit(bits, bit);
+
+               while (isblank(*p)) p++;
+               if (*p == ',')
+                       p++;
+       }
+
+       e->attrs = cpu_to_le64(attrs);
+       return 0;
+}
+
 static int gpt_get_partition(struct fdisk_context *cxt, size_t n,
                             struct fdisk_partition *pa)
 {
@@ -1378,6 +1489,75 @@ done:
 }
 
 
+static int gpt_set_partition(struct fdisk_context *cxt, size_t n,
+                            struct fdisk_partition *pa)
+{
+       struct fdisk_gpt_label *gpt;
+       struct gpt_entry *e;
+       int rc = 0;
+
+       assert(cxt);
+       assert(cxt->label);
+       assert(fdisk_is_label(cxt, GPT));
+
+       gpt = self_label(cxt);
+
+       if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
+               return -EINVAL;
+
+       gpt = self_label(cxt);
+       e = &gpt->ents[n];
+
+       if (pa->uuid) {
+               char new_u[37], old_u[37];
+
+               guid_to_string(&e->partition_guid, old_u);
+               rc = gpt_entry_set_uuid(e, pa->uuid);
+               if (rc)
+                       return rc;
+               guid_to_string(&e->partition_guid, new_u);
+               fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
+                       _("Partition UUID changed from %s to %s."),
+                       old_u, new_u);
+       }
+
+       if (pa->name) {
+               char *old = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
+               gpt_entry_set_name(e, pa->name);
+
+               fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
+                       _("Partition name changed from '%s' to '%.*s'."),
+                       old, (int) GPT_PART_NAME_LEN, pa->name);
+               free(old);
+       }
+
+       if (pa->type && pa->type->typestr) {
+               struct gpt_guid typeid;
+
+               rc = string_to_guid(pa->type->typestr, &typeid);
+               if (rc)
+                       return rc;
+               gpt_entry_set_type(e, &typeid);
+       }
+       if (pa->attrs) {
+               rc = gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+               if (rc)
+                       return rc;
+       }
+
+       if (fdisk_partition_has_start(pa))
+               e->lba_start = cpu_to_le64(pa->start);
+       if (fdisk_partition_has_size(pa))
+               e->lba_end = cpu_to_le64(gpt_partition_start(e) + pa->size - 1ULL);
+
+       gpt_recompute_crc(gpt->pheader, gpt->ents);
+       gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+       fdisk_label_set_changed(cxt->label, 1);
+       return rc;
+}
+
+
 /*
  * List label partitions.
  */
@@ -1392,8 +1572,10 @@ static int gpt_list_disklabel(struct fdisk_context *cxt)
 
                fdisk_info(cxt, _("First LBA: %ju"), h->first_usable_lba);
                fdisk_info(cxt, _("Last LBA: %ju"), h->last_usable_lba);
+               /* TRANSLATORS: The LBA (Logical Block Address) of the backup GPT header. */
                fdisk_info(cxt, _("Alternative LBA: %ju"), h->alternative_lba);
-               fdisk_info(cxt, _("Partitions entries LBA: %ju"), h->partition_entry_lba);
+               /* TRANSLATORS: The start of the array of partition entries. */
+               fdisk_info(cxt, _("Partition entries LBA: %ju"), h->partition_entry_lba);
                fdisk_info(cxt, _("Allocated partition entries: %u"), h->npartition_entries);
        }
 
@@ -1709,44 +1891,12 @@ static int gpt_delete_partition(struct fdisk_context *cxt,
        return 0;
 }
 
-static void gpt_entry_set_type(struct gpt_entry *e, struct gpt_guid *uuid)
-{
-       e->type = *uuid;
-       DBG(LABEL, gpt_debug_uuid("new type", &(e->type)));
-}
-
-static void gpt_entry_set_name(struct gpt_entry *e, char *str)
-{
-       char name[GPT_PART_NAME_LEN] = { 0 };
-       size_t i, sz = strlen(str);
-
-       if (sz) {
-               if (sz > GPT_PART_NAME_LEN)
-                       sz = GPT_PART_NAME_LEN;
-               memcpy(name, str, sz);
-       }
-
-       for (i = 0; i < GPT_PART_NAME_LEN; i++)
-               e->name[i] = cpu_to_le16((uint16_t) name[i]);
-}
-
-static int gpt_entry_set_uuid(struct gpt_entry *e, char *str)
-{
-       struct gpt_guid uuid;
-       int rc;
-
-       rc = string_to_guid(str, &uuid);
-       if (rc)
-               return rc;
-
-       e->partition_guid = uuid;
-       return 0;
-}
 
 /* Performs logical checks to add a new partition entry */
 static int gpt_add_partition(
                struct fdisk_context *cxt,
-               struct fdisk_partition *pa)
+               struct fdisk_partition *pa,
+               size_t *partno)
 {
        uint64_t user_f, user_l;        /* user input ranges for first and last sectors */
        uint64_t disk_f, disk_l;        /* first and last available sector ranges on device*/
@@ -1802,14 +1952,16 @@ static int gpt_add_partition(
        dflt_f = fdisk_align_lba_in_range(cxt, dflt_f, dflt_f, dflt_l);
 
        /* first sector */
-       if (pa && pa->start) {
+       if (pa && pa->start_follow_default) {
+               user_f = dflt_f;
+
+       } else if (pa && fdisk_partition_has_start(pa)) {
+               DBG(LABEL, ul_debug("first sector defined: %ju", pa->start));
                if (pa->start != find_first_available(pheader, ents, pa->start)) {
                        fdisk_warnx(cxt, _("Sector %ju already used."), pa->start);
                        return -ERANGE;
                }
                user_f = pa->start;
-       } else if (pa && pa->start_follow_default) {
-               user_f = dflt_f;
        } else {
                /*  ask by dialog */
                for (;;) {
@@ -1842,17 +1994,16 @@ static int gpt_add_partition(
        /* Last sector */
        dflt_l = find_last_free(pheader, ents, user_f);
 
-       if (pa && pa->size) {
-               user_l = user_f + pa->size;
-               user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
+       if (pa && pa->end_follow_default) {
+               user_l = dflt_l;
 
-               /* no space for anything useful, use all space
-               if (user_l + (cxt->grain / cxt->sector_size) > dflt_l)
-                       user_l = dflt_l;
-               */
+       } else if (pa && fdisk_partition_has_size(pa)) {
+               user_l = user_f + pa->size - 1;
+               DBG(LABEL, ul_debug("size defined: %ju, end: %ju (last possible: %ju)",
+                                       pa->size, user_l, dflt_l));
+               if (user_l != dflt_l && !pa->size_explicit)
+                       user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
 
-       } else if (pa && pa->end_follow_default) {
-               user_l = dflt_l;
        } else {
                for (;;) {
                        if (!ask)
@@ -1880,13 +2031,13 @@ static int gpt_add_partition(
                                if (user_l + (cxt->grain / cxt->sector_size) > dflt_l)
                                        user_l = dflt_l;
                                */
-                       } if (user_l > user_f && user_l <= disk_l)
+                       }
+
+                       if (user_l > user_f && user_l <= disk_l)
                                break;
                }
        }
 
-       DBG(LABEL, ul_debug("GPT new partition: partno=%zu, start=%ju, end=%ju",
-                               partnum, user_f, user_l));
 
        if (user_f > user_l || partnum >= cxt->label->nparts_max) {
                fdisk_warnx(cxt, _("Could not create partition %zu"), partnum + 1);
@@ -1894,6 +2045,9 @@ static int gpt_add_partition(
                goto done;
        }
 
+       assert(!FDISK_IS_UNDEF(user_l));
+       assert(!FDISK_IS_UNDEF(user_f));
+
        e = &ents[partnum];
        e->lba_end = cpu_to_le64(user_l);
        e->lba_start = cpu_to_le64(user_f);
@@ -1918,6 +2072,14 @@ static int gpt_add_partition(
 
        if (pa && pa->name && *pa->name)
                gpt_entry_set_name(e, pa->name);
+       if (pa && pa->attrs)
+               gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+
+       DBG(LABEL, ul_debug("GPT new partition: partno=%zu, start=%ju, end=%ju, size=%ju",
+                               partnum,
+                               gpt_partition_start(e),
+                               gpt_partition_end(e),
+                               gpt_partition_size(e)));
 
        gpt_recompute_crc(gpt->pheader, ents);
        gpt_recompute_crc(gpt->bheader, ents);
@@ -1935,6 +2097,8 @@ static int gpt_add_partition(
        }
 
        rc = 0;
+       if (partno)
+               *partno = partnum;
 done:
        fdisk_free_ask(ask);
        return rc;
@@ -2073,31 +2237,6 @@ static int gpt_set_disklabel_id(struct fdisk_context *cxt)
        return 0;
 }
 
-static int gpt_set_partition_type(
-               struct fdisk_context *cxt,
-               size_t i,
-               struct fdisk_parttype *t)
-{
-       struct gpt_guid uuid;
-       struct fdisk_gpt_label *gpt;
-
-       assert(cxt);
-       assert(cxt->label);
-       assert(fdisk_is_label(cxt, GPT));
-
-       gpt = self_label(cxt);
-       if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries)
-            || !t || !t->typestr || string_to_guid(t->typestr, &uuid) != 0)
-               return -EINVAL;
-
-       gpt_entry_set_type(&gpt->ents[i], &uuid);
-       gpt_recompute_crc(gpt->pheader, gpt->ents);
-       gpt_recompute_crc(gpt->bheader, gpt->ents);
-
-       fdisk_label_set_changed(cxt->label, 1);
-       return 0;
-}
-
 static int gpt_part_is_used(struct fdisk_context *cxt, size_t i)
 {
        struct fdisk_gpt_label *gpt;
@@ -2116,91 +2255,6 @@ static int gpt_part_is_used(struct fdisk_context *cxt, size_t i)
        return !partition_unused(e) || gpt_partition_start(e);
 }
 
-int fdisk_gpt_partition_set_uuid(struct fdisk_context *cxt, size_t i)
-{
-       struct fdisk_gpt_label *gpt;
-       struct gpt_entry *e;
-       char *str, new_u[37], old_u[37];
-       int rc;
-
-       assert(cxt);
-       assert(cxt->label);
-       assert(fdisk_is_label(cxt, GPT));
-
-       DBG(LABEL, ul_debug("UUID change requested partno=%zu", i));
-
-       gpt = self_label(cxt);
-
-       if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
-               return -EINVAL;
-
-       if (fdisk_ask_string(cxt,
-                       _("New UUID (in 8-4-4-4-12 format)"), &str))
-               return -EINVAL;
-
-       e = &gpt->ents[i];
-       guid_to_string(&e->partition_guid, old_u);
-
-       rc = gpt_entry_set_uuid(e, str);
-
-       free(str);
-
-       if (rc) {
-               fdisk_warnx(cxt, _("Failed to parse your UUID."));
-               return rc;
-       }
-
-       guid_to_string(&e->partition_guid, new_u);
-
-       gpt_recompute_crc(gpt->pheader, gpt->ents);
-       gpt_recompute_crc(gpt->bheader, gpt->ents);
-       fdisk_label_set_changed(cxt->label, 1);
-
-       fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
-                       _("Partition UUID changed from %s to %s."),
-                       old_u, new_u);
-       return 0;
-}
-
-int fdisk_gpt_partition_set_name(struct fdisk_context *cxt, size_t i)
-{
-       struct fdisk_gpt_label *gpt;
-       struct gpt_entry *e;
-       char *str, *old;
-
-       assert(cxt);
-       assert(cxt->label);
-       assert(fdisk_is_label(cxt, GPT));
-
-       DBG(LABEL, ul_debug("NAME change requested partno=%zu", i));
-
-       gpt = self_label(cxt);
-
-       if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
-               return -EINVAL;
-
-       if (fdisk_ask_string(cxt, _("New name"), &str))
-               return -EINVAL;
-
-       e = &gpt->ents[i];
-       old = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
-
-       gpt_entry_set_name(e, str);
-
-       gpt_recompute_crc(gpt->pheader, gpt->ents);
-       gpt_recompute_crc(gpt->bheader, gpt->ents);
-
-       fdisk_label_set_changed(cxt->label, 1);
-
-       fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
-                       _("Partition name changed from '%s' to '%.*s'."),
-                       old, (int) GPT_PART_NAME_LEN, str);
-       free(str);
-       free(old);
-
-       return 0;
-}
-
 int fdisk_gpt_is_hybrid(struct fdisk_context *cxt)
 {
        assert(cxt);
@@ -2393,12 +2447,11 @@ static const struct fdisk_label_operations gpt_operations =
        .set_id         = gpt_set_disklabel_id,
 
        .get_part       = gpt_get_partition,
+       .set_part       = gpt_set_partition,
        .add_part       = gpt_add_partition,
-
-       .part_delete    = gpt_delete_partition,
+       .del_part       = gpt_delete_partition,
 
        .part_is_used   = gpt_part_is_used,
-       .part_set_type  = gpt_set_partition_type,
        .part_toggle_flag = gpt_toggle_partition_flag,
 
        .deinit         = gpt_deinit,
@@ -2413,7 +2466,6 @@ static const struct fdisk_field gpt_fields[] =
        { FDISK_FIELD_START,    N_("Start"),      5,    FDISK_FIELDFL_NUMBER },
        { FDISK_FIELD_END,      N_("End"),        5,    FDISK_FIELDFL_NUMBER },
        { FDISK_FIELD_SECTORS,  N_("Sectors"),    5,    FDISK_FIELDFL_NUMBER },
-       { FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,    FDISK_FIELDFL_NUMBER },
        { FDISK_FIELD_SIZE,     N_("Size"),       5,    FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
        { FDISK_FIELD_TYPE,     N_("Type"),     0.1,    FDISK_FIELDFL_EYECANDY },
        /* expert */