diff options
Diffstat (limited to 'libfdisk/src/label.c')
-rw-r--r-- | libfdisk/src/label.c | 726 |
1 files changed, 726 insertions, 0 deletions
diff --git a/libfdisk/src/label.c b/libfdisk/src/label.c new file mode 100644 index 0000000..98ab475 --- /dev/null +++ b/libfdisk/src/label.c @@ -0,0 +1,726 @@ + +#include "fdiskP.h" + + +/** + * SECTION: label + * @title: Label + * @short_description: disk label (PT) specific data and functions + * + * The fdisk_new_context() initializes all label drivers, and allocate + * per-label specific data struct. This concept can be used to store label specific + * settings to the label driver independently on the currently active label + * driver. Note that label struct cannot be deallocated, so there is no + * reference counting for fdisk_label objects. All is destroyed by + * fdisk_unref_context() only. + * + * Anyway, all label drives share in-memory first sector. The function + * fdisk_create_disklabel() overwrites this in-memory sector. But it's possible that + * label driver also uses another buffers, for example GPT reads more sectors + * from the device. + * + * All label operations are in-memory only, except fdisk_write_disklabel(). + * + * All functions that use "struct fdisk_context" rather than "struct + * fdisk_label" use the currently active label driver. + */ + + +int fdisk_probe_labels(struct fdisk_context *cxt) +{ + size_t i; + + cxt->label = NULL; + + for (i = 0; i < cxt->nlabels; i++) { + struct fdisk_label *lb = cxt->labels[i]; + struct fdisk_label *org = fdisk_get_label(cxt, NULL); + int rc; + + if (!lb->op->probe) + continue; + if (lb->disabled) { + DBG(CXT, ul_debugobj(cxt, "%s: disabled -- ignore", lb->name)); + continue; + } + DBG(CXT, ul_debugobj(cxt, "probing for %s", lb->name)); + + cxt->label = lb; + rc = lb->op->probe(cxt); + cxt->label = org; + + if (rc != 1) { + if (lb->op->deinit) + lb->op->deinit(lb); /* for sure */ + continue; + } + + __fdisk_switch_label(cxt, lb); + return 0; + } + + DBG(CXT, ul_debugobj(cxt, "no label found")); + return 1; /* not found */ +} + +/** + * fdisk_label_get_name: + * @lb: label + * + * Returns: label name + */ +const char *fdisk_label_get_name(const struct fdisk_label *lb) +{ + return lb ? lb->name : NULL; +} + +/** + * fdisk_label_is_labeltype: + * @lb: label + * + * Returns: FDISK_DISKLABEL_*. + */ +int fdisk_label_get_type(const struct fdisk_label *lb) +{ + return lb->id; +} + +/** + * fdisk_label_require_geometry: + * @lb: label + * + * Returns: 1 if label requires CHS geometry + */ +int fdisk_label_require_geometry(const struct fdisk_label *lb) +{ + assert(lb); + + return lb->flags & FDISK_LABEL_FL_REQUIRE_GEOMETRY ? 1 : 0; +} + +/** + * fdisk_label_get_fields_ids + * @lb: label (or NULL for the current label) + * @cxt: context + * @ids: returns allocated array with FDISK_FIELD_* IDs + * @nids: returns number of items in fields + * + * This function returns the default fields for the label. + * + * Note that the set of the default fields depends on fdisk_enable_details() + * function. If the details are enabled then this function usually returns more + * fields. + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_label_get_fields_ids( + const struct fdisk_label *lb, + struct fdisk_context *cxt, + int **ids, size_t *nids) +{ + size_t i, n; + int *c; + + if (!cxt || (!lb && !cxt->label)) + return -EINVAL; + + lb = cxt->label; + if (!lb->fields || !lb->nfields) + return -ENOSYS; + c = calloc(lb->nfields, sizeof(int)); + if (!c) + return -ENOMEM; + for (n = 0, i = 0; i < lb->nfields; i++) { + int id = lb->fields[i].id; + + if ((fdisk_is_details(cxt) && + (lb->fields[i].flags & FDISK_FIELDFL_EYECANDY)) + || (!fdisk_is_details(cxt) && + (lb->fields[i].flags & FDISK_FIELDFL_DETAIL)) + || (id == FDISK_FIELD_SECTORS && + fdisk_use_cylinders(cxt)) + || (id == FDISK_FIELD_CYLINDERS && + !fdisk_use_cylinders(cxt))) + continue; + + c[n++] = id; + } + if (ids) + *ids = c; + else + free(c); + if (nids) + *nids = n; + return 0; +} + +/** + * fdisk_label_get_fields_ids_all + * @lb: label (or NULL for the current label) + * @cxt: context + * @ids: returns allocated array with FDISK_FIELD_* IDs + * @nids: returns number of items in fields + * + * This function returns all fields for the label. + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_label_get_fields_ids_all( + const struct fdisk_label *lb, + struct fdisk_context *cxt, + int **ids, size_t *nids) +{ + size_t i, n; + int *c; + + if (!cxt || (!lb && !cxt->label)) + return -EINVAL; + + lb = cxt->label; + if (!lb->fields || !lb->nfields) + return -ENOSYS; + c = calloc(lb->nfields, sizeof(int)); + if (!c) + return -ENOMEM; + for (n = 0, i = 0; i < lb->nfields; i++) + c[n++] = lb->fields[i].id; + if (ids) + *ids = c; + else + free(c); + if (nids) + *nids = n; + return 0; +} + +/** + * fdisk_label_get_field: + * @lb: label + * @id: FDISK_FIELD_* + * + * The field struct describes data stored in struct fdisk_partition. The info + * about data is usable for example to generate human readable output (e.g. + * fdisk 'p'rint command). See fdisk_partition_to_string() and fdisk code. + * + * Returns: pointer to static instance of the field. + */ +const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id) +{ + size_t i; + + assert(lb); + assert(id > 0); + + for (i = 0; i < lb->nfields; i++) { + if (lb->fields[i].id == id) + return &lb->fields[i]; + } + + return NULL; +} + +/** + * fdisk_label_get_field_by_name + * @lb: label + * @name: field name + * + * Returns: pointer to static instance of the field. + */ +const struct fdisk_field *fdisk_label_get_field_by_name( + const struct fdisk_label *lb, + const char *name) +{ + size_t i; + + assert(lb); + assert(name); + + for (i = 0; i < lb->nfields; i++) { + if (lb->fields[i].name && strcasecmp(lb->fields[i].name, name) == 0) + return &lb->fields[i]; + } + + return NULL; +} + +/** + * fdisk_write_disklabel: + * @cxt: fdisk context + * + * This function wipes the device (if enabled by fdisk_enable_wipe()) and then + * it writes in-memory changes to disk. Be careful! + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_write_disklabel(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label || cxt->readonly) + return -EINVAL; + if (!cxt->label->op->write) + return -ENOSYS; + + fdisk_do_wipe(cxt); + return cxt->label->op->write(cxt); +} + +/** + * fdisk_verify_disklabel: + * @cxt: fdisk context + * + * Verifies the partition table. + * + * Returns: 0 on success, <1 runtime or option errors, >0 number of detected issues + */ +int fdisk_verify_disklabel(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->verify) + return -ENOSYS; + if (fdisk_missing_geometry(cxt)) + return -EINVAL; + + return cxt->label->op->verify(cxt); +} + +/** + * fdisk_list_disklabel: + * @cxt: fdisk context + * + * Lists details about disklabel, but no partitions. + * + * This function is based on fdisk_get_disklabel_item() and prints all label + * specific information by ASK interface (FDISK_ASKTYPE_INFO, aka fdisk_info()). + * The function requires enabled "details" by fdisk_enable_details(). + * + * It's recommended to use fdisk_get_disklabel_item() if you need better + * control on output and formatting. + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_list_disklabel(struct fdisk_context *cxt) +{ + int id = 0, rc = 0; + struct fdisk_labelitem item = { .id = id }; + + if (!cxt || !cxt->label) + return -EINVAL; + + if (!cxt->display_details) + return 0; + + /* List all label items */ + do { + /* rc: < 0 error, 0 success, 1 unknown item, 2 out of range */ + rc = fdisk_get_disklabel_item(cxt, id++, &item); + if (rc != 0) + continue; + switch (item.type) { + case 'j': + fdisk_info(cxt, "%s: %ju", item.name, item.data.num64); + break; + case 's': + if (item.data.str && item.name) + fdisk_info(cxt, "%s: %s", item.name, item.data.str); + break; + } + fdisk_reset_labelitem(&item); + } while (rc == 0 || rc == 1); + + return rc < 0 ? rc : 0; +} + +/** + * fdisk_create_disklabel: + * @cxt: fdisk context + * @name: label name + * + * Creates a new disk label of type @name. If @name is NULL, then it will + * create a default system label type, either SUN or DOS. The function + * automatically switches the current label driver to @name. The function + * fdisk_get_label() returns the current label driver. + * + * The function modifies in-memory data only. + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name) +{ + int haslabel = 0; + struct fdisk_label *lb; + + if (!cxt) + return -EINVAL; + + if (!name) { /* use default label creation */ +#ifdef __sparc__ + name = "sun"; +#else + name = "dos"; +#endif + } + + if (cxt->label) { + fdisk_deinit_label(cxt->label); + haslabel = 1; + } + + lb = fdisk_get_label(cxt, name); + if (!lb || lb->disabled) + return -EINVAL; + + if (!haslabel || (lb && cxt->label != lb)) + fdisk_check_collisions(cxt); + + if (!lb->op->create) + return -ENOSYS; + + __fdisk_switch_label(cxt, lb); + assert(cxt->label == lb); + + if (haslabel && !cxt->parent) + fdisk_reset_device_properties(cxt); + + DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name)); + return lb->op->create(cxt); +} + +/** + * fdisk_locate_disklabel: + * @cxt: context + * @n: N item + * @name: return item name + * @offset: return offset where is item + * @size: of the item + * + * Locate disklabel and returns info about @n item of the label. For example + * GPT is composed from two items, PMBR and GPT, n=0 return offset to PMBR and n=1 + * return offset to GPT. For more details see 'D' expert fdisk command. + * + * Returns: 0 on success, <0 on error, 1 no more items. + */ +int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, + uint64_t *offset, size_t *size) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->locate) + return -ENOSYS; + + DBG(CXT, ul_debugobj(cxt, "locating %d chunk of %s.", n, cxt->label->name)); + return cxt->label->op->locate(cxt, n, name, offset, size); +} + + +/** + * fdisk_get_disklabel_id: + * @cxt: fdisk context + * @id: returns pointer to allocated string (MBR Id or GPT dirk UUID) + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id) +{ + struct fdisk_labelitem item = FDISK_LABELITEM_INIT; + int rc; + + if (!cxt || !cxt->label || !id) + return -EINVAL; + + DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name)); + + rc = fdisk_get_disklabel_item(cxt, FDISK_LABELITEM_ID, &item); + if (rc == 0) { + *id = item.data.str; + item.data.str = NULL; + } + fdisk_reset_labelitem(&item); + if (rc > 0) + rc = 0; + return rc; +} + +/** + * fdisk_get_disklabel_item: + * @cxt: fdisk context + * @id: item ID (FDISK_LABELITEM_* or *_LABELITEM_*) + * @item: specifies and returns the item + * + * Note that @id is always in range 0..N. It's fine to use the function in loop + * until it returns error or 2, the result in @item should be ignored when + * function returns 1. Don't forget to use fdisk_reset_labelitem() or fdisk_unref_labelitem(). + * + * Returns: 0 on success, < 0 on error, 1 on unsupported item, 2 id out of range + */ +int fdisk_get_disklabel_item(struct fdisk_context *cxt, int id, struct fdisk_labelitem *item) +{ + if (!cxt || !cxt->label || !item) + return -EINVAL; + + fdisk_reset_labelitem(item); + item->id = id; + DBG(CXT, ul_debugobj(cxt, "asking for disk %s item %d", cxt->label->name, item->id)); + + if (!cxt->label->op->get_item) + return -ENOSYS; + + return cxt->label->op->get_item(cxt, item); +} + +/** + * fdisk_set_disklabel_id: + * @cxt: fdisk context + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_set_disklabel_id(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->set_id) + return -ENOSYS; + + DBG(CXT, ul_debugobj(cxt, "setting %s disk ID", cxt->label->name)); + return cxt->label->op->set_id(cxt, NULL); +} + +/** + * fdisk_set_disklabel_id_from_string + * @cxt: fdisk context + * @str: new Id + * + * Returns: 0 on success, otherwise, a corresponding error. + * + * Since: 2.36 + */ +int fdisk_set_disklabel_id_from_string(struct fdisk_context *cxt, const char *str) +{ + if (!cxt || !cxt->label || !str) + return -EINVAL; + if (!cxt->label->op->set_id) + return -ENOSYS; + + DBG(CXT, ul_debugobj(cxt, "setting %s disk ID from '%s'", cxt->label->name, str)); + return cxt->label->op->set_id(cxt, str); +} + +/** + * fdisk_set_partition_type: + * @cxt: fdisk context + * @partnum: partition number + * @t: new type + * + * Returns: 0 on success, < 0 on error. + */ +int fdisk_set_partition_type(struct fdisk_context *cxt, + size_t partnum, + struct fdisk_parttype *t) +{ + if (!cxt || !cxt->label || !t) + return -EINVAL; + + + if (cxt->label->op->set_part) { + struct fdisk_partition *pa = fdisk_new_partition(); + int rc; + + if (!pa) + return -ENOMEM; + fdisk_partition_set_type(pa, t); + + DBG(CXT, ul_debugobj(cxt, "partition: %zd: set type", partnum)); + rc = cxt->label->op->set_part(cxt, partnum, pa); + fdisk_unref_partition(pa); + return rc; + } + + return -ENOSYS; +} + + +/** + * fdisk_toggle_partition_flag: + * @cxt: fdisk context + * @partnum: partition number + * @flag: flag ID + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_toggle_partition_flag(struct fdisk_context *cxt, + size_t partnum, + unsigned long flag) +{ + int rc; + + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->part_toggle_flag) + return -ENOSYS; + + rc = cxt->label->op->part_toggle_flag(cxt, partnum, flag); + + DBG(CXT, ul_debugobj(cxt, "partition: %zd: toggle: 0x%04lx [rc=%d]", partnum, flag, rc)); + return rc; +} + +/** + * fdisk_reorder_partitions + * @cxt: fdisk context + * + * Sort partitions according to the partition start sector. + * + * Returns: 0 on success, 1 reorder unnecessary, otherwise a corresponding error. + */ +int fdisk_reorder_partitions(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->reorder) + return -ENOSYS; + + return cxt->label->op->reorder(cxt); +} + +/* + * Resets the current used label driver to initial state + */ +void fdisk_deinit_label(struct fdisk_label *lb) +{ + assert(lb); + + /* private label information */ + if (lb->op->deinit) + lb->op->deinit(lb); +} + +/** + * fdisk_label_set_changed: + * @lb: label + * @changed: 0/1 + * + * Marks in-memory data as changed, to force fdisk_write_disklabel() to write + * to device. This should be unnecessary by default, the library keeps track + * about changes. + */ +void fdisk_label_set_changed(struct fdisk_label *lb, int changed) +{ + assert(lb); + lb->changed = changed ? 1 : 0; +} + +/** + * fdisk_label_is_changed: + * @lb: label + * + * Returns: 1 if in-memory data has been changed. + */ +int fdisk_label_is_changed(const struct fdisk_label *lb) +{ + return lb ? lb->changed : 0; +} + +/** + * fdisk_label_set_disabled: + * @lb: label + * @disabled: 0 or 1 + * + * Mark label as disabled, then libfdisk is going to ignore the label when + * probe device for labels. + */ +void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled) +{ + assert(lb); + + DBG(LABEL, ul_debug("%s label %s", + lb->name, + disabled ? "DISABLED" : "ENABLED")); + lb->disabled = disabled ? 1 : 0; +} + +/** + * fdisk_label_is_disabled: + * @lb: label + * + * Returns: 1 if label driver disabled. + */ +int fdisk_label_is_disabled(const struct fdisk_label *lb) +{ + assert(lb); + return lb ? lb->disabled : 0; +} + +/** + * fdisk_label_get_geomrange_sectors: + * @lb: label + * @mi: minimal number + * @ma: maximal number + * + * The function provides minimal and maximal geometry supported for the label, + * if no range defined by library then returns -ENOSYS. + * + * Since: 2.32 + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_label_get_geomrange_sectors(const struct fdisk_label *lb, + fdisk_sector_t *mi, fdisk_sector_t *ma) +{ + if (!lb || lb->geom_min.sectors == 0) + return -ENOSYS; + if (mi) + *mi = lb->geom_min.sectors; + if (ma) + *ma = lb->geom_max.sectors; + return 0; +} + +/** + * fdisk_label_get_geomrange_heads: + * @lb: label + * @mi: minimal number + * @ma: maximal number + * + * The function provides minimal and maximal geometry supported for the label, + * if no range defined by library then returns -ENOSYS. + * + * Since: 2.32 + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_label_get_geomrange_heads(const struct fdisk_label *lb, + unsigned int *mi, unsigned int *ma) +{ + if (!lb || lb->geom_min.heads == 0) + return -ENOSYS; + if (mi) + *mi = lb->geom_min.heads; + if (ma) + *ma = lb->geom_max.heads; + return 0; +} + +/** + * fdisk_label_get_geomrange_cylinders: + * @lb: label + * @mi: minimal number + * @ma: maximal number + * + * The function provides minimal and maximal geometry supported for the label, + * if no range defined by library then returns -ENOSYS. + * + * Since: 2.32 + * + * Returns: 0 on success, otherwise, a corresponding error. + */ +int fdisk_label_get_geomrange_cylinders(const struct fdisk_label *lb, + fdisk_sector_t *mi, fdisk_sector_t *ma) +{ + if (!lb || lb->geom_min.cylinders == 0) + return -ENOSYS; + if (mi) + *mi = lb->geom_min.cylinders; + if (ma) + *ma = lb->geom_max.cylinders; + return 0; +} + |