| |
| #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 allows 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 the sector. But it's possible that |
| * label driver also uses another buffers, for example GPT uses more than only |
| * the first sector. |
| * |
| * 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; |
| |
| assert(cxt); |
| |
| if (!lb) |
| lb = cxt->label; |
| if (!lb) |
| return -EINVAL; |
| 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_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_stirng() 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_field_get_id: |
| * @field: field instance |
| * |
| * Returns: field Id (FDISK_FIELD_*) |
| */ |
| int fdisk_field_get_id(const struct fdisk_field *field) |
| { |
| return field ? field->id : -EINVAL; |
| } |
| |
| /** |
| * fdisk_field_get_name: |
| * @field: field instance |
| * |
| * Returns: field name |
| */ |
| const char *fdisk_field_get_name(const struct fdisk_field *field) |
| { |
| return field ? field->name : NULL; |
| } |
| |
| /** |
| * fdisk_field_get_width: |
| * @field: field instance |
| * |
| * Returns: libsmartcols compatible width. |
| */ |
| double fdisk_field_get_width(const struct fdisk_field *field) |
| { |
| return field ? field->width : -EINVAL; |
| } |
| |
| /** |
| * fdisk_field_is_number: |
| * @field: field instance |
| * |
| * Returns: 1 if field represent number |
| */ |
| int fdisk_field_is_number(const struct fdisk_field *field) |
| { |
| return field->flags ? field->flags & FDISK_FIELDFL_NUMBER : 0; |
| } |
| |
| |
| /** |
| * fdisk_write_disklabel: |
| * @cxt: fdisk context |
| * |
| * Write 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; |
| return cxt->label->op->write(cxt); |
| } |
| |
| /** |
| * fdisk_verify_disklabel: |
| * @cxt: fdisk context |
| * |
| * Verifies the partition table. |
| * |
| * Returns: 0 on success, otherwise, a corresponding error. |
| */ |
| 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 uses libfdisk ASK interface to print data. The details about |
| * partitions table are printed by FDISK_ASKTYPE_INFO. |
| * |
| * Returns: 0 on success, otherwise, a corresponding error. |
| */ |
| int fdisk_list_disklabel(struct fdisk_context *cxt) |
| { |
| if (!cxt || !cxt->label) |
| return -EINVAL; |
| if (!cxt->label->op->list) |
| return -ENOSYS; |
| |
| return cxt->label->op->list(cxt); |
| } |
| |
| /** |
| * 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 |
| * automaticaly 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 (!lb->op->create) |
| return -ENOSYS; |
| |
| __fdisk_switch_label(cxt, lb); |
| |
| if (haslabel && !cxt->parent) |
| fdisk_reset_device_properties(cxt); |
| |
| DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name)); |
| return cxt->label->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' expect fdisk command. |
| * |
| * Returns: 0 on succes, <0 on error, 1 no more items. |
| */ |
| int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, |
| off_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) |
| { |
| if (!cxt || !cxt->label) |
| return -EINVAL; |
| if (!cxt->label->op->get_id) |
| return -ENOSYS; |
| |
| DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name)); |
| return cxt->label->op->get_id(cxt, id); |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * 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, 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 unnecessar 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) |
| { |
| assert(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; |
| } |