1
2 #include "fdiskP.h"
3
4
5 /**
6 * SECTION: label
7 * @title: Label
8 * @short_description: disk label (PT) specific data and functions
9 *
10 * The fdisk_new_context() initializes all label drivers, and allocate
11 * per-label specific data struct. This concept can be used to store label specific
12 * settings to the label driver independently on the currently active label
13 * driver. Note that label struct cannot be deallocated, so there is no
14 * reference counting for fdisk_label objects. All is destroyed by
15 * fdisk_unref_context() only.
16 *
17 * Anyway, all label drives share in-memory first sector. The function
18 * fdisk_create_disklabel() overwrites this in-memory sector. But it's possible that
19 * label driver also uses another buffers, for example GPT reads more sectors
20 * from the device.
21 *
22 * All label operations are in-memory only, except fdisk_write_disklabel().
23 *
24 * All functions that use "struct fdisk_context" rather than "struct
25 * fdisk_label" use the currently active label driver.
26 */
27
28
29 int fdisk_probe_labels(struct fdisk_context *cxt)
30 {
31 size_t i;
32
33 cxt->label = NULL;
34
35 for (i = 0; i < cxt->nlabels; i++) {
36 struct fdisk_label *lb = cxt->labels[i];
37 struct fdisk_label *org = fdisk_get_label(cxt, NULL);
38 int rc;
39
40 if (!lb->op->probe)
41 continue;
42 if (lb->disabled) {
43 DBG(CXT, ul_debugobj(cxt, "%s: disabled -- ignore", lb->name));
44 continue;
45 }
46 DBG(CXT, ul_debugobj(cxt, "probing for %s", lb->name));
47
48 cxt->label = lb;
49 rc = lb->op->probe(cxt);
50 cxt->label = org;
51
52 if (rc != 1) {
53 if (lb->op->deinit)
54 lb->op->deinit(lb); /* for sure */
55 continue;
56 }
57
58 __fdisk_switch_label(cxt, lb);
59 return 0;
60 }
61
62 DBG(CXT, ul_debugobj(cxt, "no label found"));
63 return 1; /* not found */
64 }
65
66 /**
67 * fdisk_label_get_name:
68 * @lb: label
69 *
70 * Returns: label name
71 */
72 const char *fdisk_label_get_name(const struct fdisk_label *lb)
73 {
74 return lb ? lb->name : NULL;
75 }
76
77 /**
78 * fdisk_label_is_labeltype:
79 * @lb: label
80 *
81 * Returns: FDISK_DISKLABEL_*.
82 */
83 int fdisk_label_get_type(const struct fdisk_label *lb)
84 {
85 return lb->id;
86 }
87
88 /**
89 * fdisk_label_require_geometry:
90 * @lb: label
91 *
92 * Returns: 1 if label requires CHS geometry
93 */
94 int fdisk_label_require_geometry(const struct fdisk_label *lb)
95 {
96 assert(lb);
97
98 return lb->flags & FDISK_LABEL_FL_REQUIRE_GEOMETRY ? 1 : 0;
99 }
100
101 /**
102 * fdisk_label_get_fields_ids
103 * @lb: label (or NULL for the current label)
104 * @cxt: context
105 * @ids: returns allocated array with FDISK_FIELD_* IDs
106 * @nids: returns number of items in fields
107 *
108 * This function returns the default fields for the label.
109 *
110 * Note that the set of the default fields depends on fdisk_enable_details()
111 * function. If the details are enabled then this function usually returns more
112 * fields.
113 *
114 * Returns: 0 on success, otherwise, a corresponding error.
115 */
116 int fdisk_label_get_fields_ids(
117 const struct fdisk_label *lb,
118 struct fdisk_context *cxt,
119 int **ids, size_t *nids)
120 {
121 size_t i, n;
122 int *c;
123
124 if (!cxt || (!lb && !cxt->label))
125 return -EINVAL;
126
127 lb = cxt->label;
128 if (!lb->fields || !lb->nfields)
129 return -ENOSYS;
130 c = calloc(lb->nfields, sizeof(int));
131 if (!c)
132 return -ENOMEM;
133 for (n = 0, i = 0; i < lb->nfields; i++) {
134 int id = lb->fields[i].id;
135
136 if ((fdisk_is_details(cxt) &&
137 (lb->fields[i].flags & FDISK_FIELDFL_EYECANDY))
138 || (!fdisk_is_details(cxt) &&
139 (lb->fields[i].flags & FDISK_FIELDFL_DETAIL))
140 || (id == FDISK_FIELD_SECTORS &&
141 fdisk_use_cylinders(cxt))
142 || (id == FDISK_FIELD_CYLINDERS &&
143 !fdisk_use_cylinders(cxt)))
144 continue;
145
146 c[n++] = id;
147 }
148 if (ids)
149 *ids = c;
150 else
151 free(c);
152 if (nids)
153 *nids = n;
154 return 0;
155 }
156
157 /**
158 * fdisk_label_get_fields_ids_all
159 * @lb: label (or NULL for the current label)
160 * @cxt: context
161 * @ids: returns allocated array with FDISK_FIELD_* IDs
162 * @nids: returns number of items in fields
163 *
164 * This function returns all fields for the label.
165 *
166 * Returns: 0 on success, otherwise, a corresponding error.
167 */
168 int fdisk_label_get_fields_ids_all(
169 const struct fdisk_label *lb,
170 struct fdisk_context *cxt,
171 int **ids, size_t *nids)
172 {
173 size_t i, n;
174 int *c;
175
176 if (!cxt || (!lb && !cxt->label))
177 return -EINVAL;
178
179 lb = cxt->label;
180 if (!lb->fields || !lb->nfields)
181 return -ENOSYS;
182 c = calloc(lb->nfields, sizeof(int));
183 if (!c)
184 return -ENOMEM;
185 for (n = 0, i = 0; i < lb->nfields; i++)
186 c[n++] = lb->fields[i].id;
187 if (ids)
188 *ids = c;
189 else
190 free(c);
191 if (nids)
192 *nids = n;
193 return 0;
194 }
195
196 /**
197 * fdisk_label_get_field:
198 * @lb: label
199 * @id: FDISK_FIELD_*
200 *
201 * The field struct describes data stored in struct fdisk_partition. The info
202 * about data is usable for example to generate human readable output (e.g.
203 * fdisk 'p'rint command). See fdisk_partition_to_string() and fdisk code.
204 *
205 * Returns: pointer to static instance of the field.
206 */
207 const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id)
208 {
209 size_t i;
210
211 assert(lb);
212 assert(id > 0);
213
214 for (i = 0; i < lb->nfields; i++) {
215 if (lb->fields[i].id == id)
216 return &lb->fields[i];
217 }
218
219 return NULL;
220 }
221
222 /**
223 * fdisk_label_get_field_by_name
224 * @lb: label
225 * @name: field name
226 *
227 * Returns: pointer to static instance of the field.
228 */
229 const struct fdisk_field *fdisk_label_get_field_by_name(
230 const struct fdisk_label *lb,
231 const char *name)
232 {
233 size_t i;
234
235 assert(lb);
236 assert(name);
237
238 for (i = 0; i < lb->nfields; i++) {
239 if (lb->fields[i].name && strcasecmp(lb->fields[i].name, name) == 0)
240 return &lb->fields[i];
241 }
242
243 return NULL;
244 }
245
246 /**
247 * fdisk_write_disklabel:
248 * @cxt: fdisk context
249 *
250 * This function wipes the device (if enabled by fdisk_enable_wipe()) and then
251 * it writes in-memory changes to disk. Be careful!
252 *
253 * Returns: 0 on success, otherwise, a corresponding error.
254 */
255 int fdisk_write_disklabel(struct fdisk_context *cxt)
256 {
257 if (!cxt || !cxt->label || cxt->readonly)
258 return -EINVAL;
259 if (!cxt->label->op->write)
260 return -ENOSYS;
261
262 fdisk_do_wipe(cxt);
263 return cxt->label->op->write(cxt);
264 }
265
266 /**
267 * fdisk_verify_disklabel:
268 * @cxt: fdisk context
269 *
270 * Verifies the partition table.
271 *
272 * Returns: 0 on success, <1 runtime or option errors, >0 number of detected issues
273 */
274 int fdisk_verify_disklabel(struct fdisk_context *cxt)
275 {
276 if (!cxt || !cxt->label)
277 return -EINVAL;
278 if (!cxt->label->op->verify)
279 return -ENOSYS;
280 if (fdisk_missing_geometry(cxt))
281 return -EINVAL;
282
283 return cxt->label->op->verify(cxt);
284 }
285
286 /**
287 * fdisk_list_disklabel:
288 * @cxt: fdisk context
289 *
290 * Lists details about disklabel, but no partitions.
291 *
292 * This function is based on fdisk_get_disklabel_item() and prints all label
293 * specific information by ASK interface (FDISK_ASKTYPE_INFO, aka fdisk_info()).
294 * The function requires enabled "details" by fdisk_enable_details().
295 *
296 * It's recommended to use fdisk_get_disklabel_item() if you need better
297 * control on output and formatting.
298 *
299 * Returns: 0 on success, otherwise, a corresponding error.
300 */
301 int fdisk_list_disklabel(struct fdisk_context *cxt)
302 {
303 int id = 0, rc = 0;
304 struct fdisk_labelitem item = { .id = id };
305
306 if (!cxt || !cxt->label)
307 return -EINVAL;
308
309 if (!cxt->display_details)
310 return 0;
311
312 /* List all label items */
313 do {
314 /* rc: < 0 error, 0 success, 1 unknown item, 2 out of range */
315 rc = fdisk_get_disklabel_item(cxt, id++, &item);
316 if (rc != 0)
317 continue;
318 switch (item.type) {
319 case 'j':
320 fdisk_info(cxt, "%s: %ju", item.name, item.data.num64);
321 break;
322 case 's':
323 if (item.data.str && item.name)
324 fdisk_info(cxt, "%s: %s", item.name, item.data.str);
325 break;
326 }
327 fdisk_reset_labelitem(&item);
328 } while (rc == 0 || rc == 1);
329
330 return rc < 0 ? rc : 0;
331 }
332
333 /**
334 * fdisk_create_disklabel:
335 * @cxt: fdisk context
336 * @name: label name
337 *
338 * Creates a new disk label of type @name. If @name is NULL, then it will
339 * create a default system label type, either SUN or DOS. The function
340 * automatically switches the current label driver to @name. The function
341 * fdisk_get_label() returns the current label driver.
342 *
343 * The function modifies in-memory data only.
344 *
345 * Returns: 0 on success, otherwise, a corresponding error.
346 */
347 int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name)
348 {
349 int haslabel = 0;
350 struct fdisk_label *lb;
351
352 if (!cxt)
353 return -EINVAL;
354
355 if (!name) { /* use default label creation */
356 #ifdef __sparc__
357 name = "sun";
358 #else
359 name = "dos";
360 #endif
361 }
362
363 if (cxt->label) {
364 fdisk_deinit_label(cxt->label);
365 haslabel = 1;
366 }
367
368 lb = fdisk_get_label(cxt, name);
369 if (!lb || lb->disabled)
370 return -EINVAL;
371
372 if (!haslabel || (lb && cxt->label != lb))
373 fdisk_check_collisions(cxt);
374
375 if (!lb->op->create)
376 return -ENOSYS;
377
378 __fdisk_switch_label(cxt, lb);
379 assert(cxt->label == lb);
380
381 if (haslabel && !cxt->parent)
382 fdisk_reset_device_properties(cxt);
383
384 DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name));
385 return lb->op->create(cxt);
386 }
387
388 /**
389 * fdisk_locate_disklabel:
390 * @cxt: context
391 * @n: N item
392 * @name: return item name
393 * @offset: return offset where is item
394 * @size: of the item
395 *
396 * Locate disklabel and returns info about @n item of the label.
397 *
398 * For example GPT is composed from three items, PMBR and GPT, n=0 return
399 * offset to PMBR and n=1 return offset to GPT Header and n=2 returns offset to
400 * GPT array of partitions, n=3 and n=4 returns location of the backup GPT
401 * label at the end of the disk.
402 *
403 * The function returns the current in-memory situation. It's possible that a
404 * header location is modified by write operation, for example when enabled
405 * minimization (see fdisk_gpt_enable_minimize()). In this case it's better to
406 * call this function after fdisk_write_disklabel().
407 *
408 * For more details see 'D' expert fdisk command.
409 *
410 * Returns: 0 on success, <0 on error, 1 no more items.
411 */
412 int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name,
413 uint64_t *offset, size_t *size)
414 {
415 if (!cxt || !cxt->label)
416 return -EINVAL;
417 if (!cxt->label->op->locate)
418 return -ENOSYS;
419
420 DBG(CXT, ul_debugobj(cxt, "locating %d chunk of %s.", n, cxt->label->name));
421 return cxt->label->op->locate(cxt, n, name, offset, size);
422 }
423
424
425 /**
426 * fdisk_get_disklabel_id:
427 * @cxt: fdisk context
428 * @id: returns pointer to allocated string (MBR Id or GPT dirk UUID)
429 *
430 * Returns: 0 on success, otherwise, a corresponding error.
431 */
432 int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
433 {
434 struct fdisk_labelitem item = FDISK_LABELITEM_INIT;
435 int rc;
436
437 if (!cxt || !cxt->label || !id)
438 return -EINVAL;
439
440 DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name));
441
442 rc = fdisk_get_disklabel_item(cxt, FDISK_LABELITEM_ID, &item);
443 if (rc == 0) {
444 *id = item.data.str;
445 item.data.str = NULL;
446 }
447 fdisk_reset_labelitem(&item);
448 if (rc > 0)
449 rc = 0;
450 return rc;
451 }
452
453 /**
454 * fdisk_get_disklabel_item:
455 * @cxt: fdisk context
456 * @id: item ID (FDISK_LABELITEM_* or *_LABELITEM_*)
457 * @item: specifies and returns the item
458 *
459 * Note that @id is always in range 0..N. It's fine to use the function in loop
460 * until it returns error or 2, the result in @item should be ignored when
461 * function returns 1. Don't forget to use fdisk_reset_labelitem() or fdisk_unref_labelitem().
462 *
463 * Returns: 0 on success, < 0 on error, 1 on unsupported item, 2 id out of range
464 */
465 int fdisk_get_disklabel_item(struct fdisk_context *cxt, int id, struct fdisk_labelitem *item)
466 {
467 if (!cxt || !cxt->label || !item)
468 return -EINVAL;
469
470 fdisk_reset_labelitem(item);
471 item->id = id;
472 DBG(CXT, ul_debugobj(cxt, "asking for disk %s item %d", cxt->label->name, item->id));
473
474 if (!cxt->label->op->get_item)
475 return -ENOSYS;
476
477 return cxt->label->op->get_item(cxt, item);
478 }
479
480 /**
481 * fdisk_set_disklabel_id:
482 * @cxt: fdisk context
483 *
484 * Returns: 0 on success, otherwise, a corresponding error.
485 */
486 int fdisk_set_disklabel_id(struct fdisk_context *cxt)
487 {
488 if (!cxt || !cxt->label)
489 return -EINVAL;
490 if (!cxt->label->op->set_id)
491 return -ENOSYS;
492
493 DBG(CXT, ul_debugobj(cxt, "setting %s disk ID", cxt->label->name));
494 return cxt->label->op->set_id(cxt, NULL);
495 }
496
497 /**
498 * fdisk_set_disklabel_id_from_string
499 * @cxt: fdisk context
500 * @str: new Id
501 *
502 * Returns: 0 on success, otherwise, a corresponding error.
503 *
504 * Since: 2.36
505 */
506 int fdisk_set_disklabel_id_from_string(struct fdisk_context *cxt, const char *str)
507 {
508 if (!cxt || !cxt->label || !str)
509 return -EINVAL;
510 if (!cxt->label->op->set_id)
511 return -ENOSYS;
512
513 DBG(CXT, ul_debugobj(cxt, "setting %s disk ID from '%s'", cxt->label->name, str));
514 return cxt->label->op->set_id(cxt, str);
515 }
516
517 /**
518 * fdisk_set_partition_type:
519 * @cxt: fdisk context
520 * @partnum: partition number
521 * @t: new type
522 *
523 * Returns: 0 on success, < 0 on error.
524 */
525 int fdisk_set_partition_type(struct fdisk_context *cxt,
526 size_t partnum,
527 struct fdisk_parttype *t)
528 {
529 if (!cxt || !cxt->label || !t)
530 return -EINVAL;
531
532
533 if (cxt->label->op->set_part) {
534 struct fdisk_partition *pa = fdisk_new_partition();
535 int rc;
536
537 if (!pa)
538 return -ENOMEM;
539 fdisk_partition_set_type(pa, t);
540
541 DBG(CXT, ul_debugobj(cxt, "partition: %zd: set type", partnum));
542 rc = cxt->label->op->set_part(cxt, partnum, pa);
543 fdisk_unref_partition(pa);
544 return rc;
545 }
546
547 return -ENOSYS;
548 }
549
550
551 /**
552 * fdisk_toggle_partition_flag:
553 * @cxt: fdisk context
554 * @partnum: partition number
555 * @flag: flag ID
556 *
557 * Returns: 0 on success, otherwise, a corresponding error.
558 */
559 int fdisk_toggle_partition_flag(struct fdisk_context *cxt,
560 size_t partnum,
561 unsigned long flag)
562 {
563 int rc;
564
565 if (!cxt || !cxt->label)
566 return -EINVAL;
567 if (!cxt->label->op->part_toggle_flag)
568 return -ENOSYS;
569
570 rc = cxt->label->op->part_toggle_flag(cxt, partnum, flag);
571
572 DBG(CXT, ul_debugobj(cxt, "partition: %zd: toggle: 0x%04lx [rc=%d]", partnum, flag, rc));
573 return rc;
574 }
575
576 /**
577 * fdisk_reorder_partitions
578 * @cxt: fdisk context
579 *
580 * Sort partitions according to the partition start sector.
581 *
582 * Returns: 0 on success, 1 reorder unnecessary, otherwise a corresponding error.
583 */
584 int fdisk_reorder_partitions(struct fdisk_context *cxt)
585 {
586 int rc;
587
588 if (!cxt || !cxt->label)
589 return -EINVAL;
590 if (!cxt->label->op->reorder)
591 return -ENOSYS;
592
593 rc = cxt->label->op->reorder(cxt);
594
595 switch (rc) {
596 case 0:
597 fdisk_info(cxt, _("Partitions order fixed."));
598 break;
599 case 1:
600 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
601 break;
602 default:
603 fdisk_warnx(cxt, _("Failed to fix partitions order."));
604 break;
605 }
606
607 return rc;
608 }
609
610 /*
611 * Resets the current used label driver to initial state
612 */
613 void fdisk_deinit_label(struct fdisk_label *lb)
614 {
615 assert(lb);
616
617 /* private label information */
618 if (lb->op->deinit)
619 lb->op->deinit(lb);
620 }
621
622 /**
623 * fdisk_label_set_changed:
624 * @lb: label
625 * @changed: 0/1
626 *
627 * Marks in-memory data as changed, to force fdisk_write_disklabel() to write
628 * to device. This should be unnecessary by default, the library keeps track
629 * about changes.
630 */
631 void fdisk_label_set_changed(struct fdisk_label *lb, int changed)
632 {
633 assert(lb);
634 lb->changed = changed ? 1 : 0;
635 }
636
637 /**
638 * fdisk_label_is_changed:
639 * @lb: label
640 *
641 * Returns: 1 if in-memory data has been changed.
642 */
643 int fdisk_label_is_changed(const struct fdisk_label *lb)
644 {
645 return lb ? lb->changed : 0;
646 }
647
648 /**
649 * fdisk_label_set_disabled:
650 * @lb: label
651 * @disabled: 0 or 1
652 *
653 * Mark label as disabled, then libfdisk is going to ignore the label when
654 * probe device for labels.
655 */
656 void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled)
657 {
658 assert(lb);
659
660 DBG(LABEL, ul_debug("%s label %s",
661 lb->name,
662 disabled ? "DISABLED" : "ENABLED"));
663 lb->disabled = disabled ? 1 : 0;
664 }
665
666 /**
667 * fdisk_label_is_disabled:
668 * @lb: label
669 *
670 * Returns: 1 if label driver disabled.
671 */
672 int fdisk_label_is_disabled(const struct fdisk_label *lb)
673 {
674 assert(lb);
675 return lb ? lb->disabled : 0;
676 }
677
678 /**
679 * fdisk_label_get_geomrange_sectors:
680 * @lb: label
681 * @mi: minimal number
682 * @ma: maximal number
683 *
684 * The function provides minimal and maximal geometry supported for the label,
685 * if no range defined by library then returns -ENOSYS.
686 *
687 * Since: 2.32
688 *
689 * Returns: 0 on success, otherwise, a corresponding error.
690 */
691 int fdisk_label_get_geomrange_sectors(const struct fdisk_label *lb,
692 fdisk_sector_t *mi, fdisk_sector_t *ma)
693 {
694 if (!lb || lb->geom_min.sectors == 0)
695 return -ENOSYS;
696 if (mi)
697 *mi = lb->geom_min.sectors;
698 if (ma)
699 *ma = lb->geom_max.sectors;
700 return 0;
701 }
702
703 /**
704 * fdisk_label_get_geomrange_heads:
705 * @lb: label
706 * @mi: minimal number
707 * @ma: maximal number
708 *
709 * The function provides minimal and maximal geometry supported for the label,
710 * if no range defined by library then returns -ENOSYS.
711 *
712 * Since: 2.32
713 *
714 * Returns: 0 on success, otherwise, a corresponding error.
715 */
716 int fdisk_label_get_geomrange_heads(const struct fdisk_label *lb,
717 unsigned int *mi, unsigned int *ma)
718 {
719 if (!lb || lb->geom_min.heads == 0)
720 return -ENOSYS;
721 if (mi)
722 *mi = lb->geom_min.heads;
723 if (ma)
724 *ma = lb->geom_max.heads;
725 return 0;
726 }
727
728 /**
729 * fdisk_label_get_geomrange_cylinders:
730 * @lb: label
731 * @mi: minimal number
732 * @ma: maximal number
733 *
734 * The function provides minimal and maximal geometry supported for the label,
735 * if no range defined by library then returns -ENOSYS.
736 *
737 * Since: 2.32
738 *
739 * Returns: 0 on success, otherwise, a corresponding error.
740 */
741 int fdisk_label_get_geomrange_cylinders(const struct fdisk_label *lb,
742 fdisk_sector_t *mi, fdisk_sector_t *ma)
743 {
744 if (!lb || lb->geom_min.cylinders == 0)
745 return -ENOSYS;
746 if (mi)
747 *mi = lb->geom_min.cylinders;
748 if (ma)
749 *ma = lb->geom_max.cylinders;
750 return 0;
751 }
752