]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/label.c
2a11acad6bb7fbb357e74c9164b6936ec7fe92d9
[thirdparty/util-linux.git] / libfdisk / src / label.c
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 allows 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 * Write in-memory changes to disk. Be careful!
251 *
252 * Returns: 0 on success, otherwise, a corresponding error.
253 */
254 int fdisk_write_disklabel(struct fdisk_context *cxt)
255 {
256 if (!cxt || !cxt->label || cxt->readonly)
257 return -EINVAL;
258 if (!cxt->label->op->write)
259 return -ENOSYS;
260
261 fdisk_do_wipe(cxt);
262 return cxt->label->op->write(cxt);
263 }
264
265 /**
266 * fdisk_verify_disklabel:
267 * @cxt: fdisk context
268 *
269 * Verifies the partition table.
270 *
271 * Returns: 0 on success, otherwise, a corresponding error.
272 */
273 int fdisk_verify_disklabel(struct fdisk_context *cxt)
274 {
275 if (!cxt || !cxt->label)
276 return -EINVAL;
277 if (!cxt->label->op->verify)
278 return -ENOSYS;
279 if (fdisk_missing_geometry(cxt))
280 return -EINVAL;
281
282 return cxt->label->op->verify(cxt);
283 }
284
285 /**
286 * fdisk_list_disklabel:
287 * @cxt: fdisk context
288 *
289 * Lists details about disklabel, but no partitions.
290 *
291 * This function is based on fdisk_get_disklabel_item() and prints all label
292 * specific information by ASK interface (FDISK_ASKTYPE_INFO, aka fdisk_info()).
293 * The function requires enabled "details" by fdisk_enable_details().
294 *
295 * It's recommended to use fdisk_get_disklabel_item() if you need better
296 * control on output and formatting.
297 *
298 * Returns: 0 on success, otherwise, a corresponding error.
299 */
300 int fdisk_list_disklabel(struct fdisk_context *cxt)
301 {
302 int id = 0, rc = 0;
303 struct fdisk_labelitem item = { .id = id };
304
305 if (!cxt || !cxt->label)
306 return -EINVAL;
307
308 if (!cxt->display_details)
309 return 0;
310
311 /* List all label items */
312 do {
313 /* rc: < 0 error, 0 success, 1 unknown item, 2 out of range */
314 rc = fdisk_get_disklabel_item(cxt, id++, &item);
315 if (rc != 0)
316 continue;
317 switch (item.type) {
318 case 'j':
319 fdisk_info(cxt, "%s: %ju", item.name, item.data.num64);
320 break;
321 case 's':
322 if (item.data.str && item.name)
323 fdisk_info(cxt, "%s: %s", item.name, item.data.str);
324 break;
325 }
326 fdisk_reset_labelitem(&item);
327 } while (rc == 0 || rc == 1);
328
329 return rc < 0 ? rc : 0;
330 }
331
332 /**
333 * fdisk_create_disklabel:
334 * @cxt: fdisk context
335 * @name: label name
336 *
337 * Creates a new disk label of type @name. If @name is NULL, then it will
338 * create a default system label type, either SUN or DOS. The function
339 * automatically switches the current label driver to @name. The function
340 * fdisk_get_label() returns the current label driver.
341 *
342 * The function modifies in-memory data only.
343 *
344 * Returns: 0 on success, otherwise, a corresponding error.
345 */
346 int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name)
347 {
348 int haslabel = 0;
349 struct fdisk_label *lb;
350
351 if (!cxt)
352 return -EINVAL;
353
354 if (!name) { /* use default label creation */
355 #ifdef __sparc__
356 name = "sun";
357 #else
358 name = "dos";
359 #endif
360 }
361
362 if (cxt->label) {
363 fdisk_deinit_label(cxt->label);
364 haslabel = 1;
365 }
366
367 lb = fdisk_get_label(cxt, name);
368 if (!lb || lb->disabled)
369 return -EINVAL;
370
371 if (!haslabel || (lb && cxt->label != lb))
372 fdisk_check_collisions(cxt);
373
374 if (!lb->op->create)
375 return -ENOSYS;
376
377 __fdisk_switch_label(cxt, lb);
378 assert(cxt->label == lb);
379
380 if (haslabel && !cxt->parent)
381 fdisk_reset_device_properties(cxt);
382
383 DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name));
384 return lb->op->create(cxt);
385 }
386
387 /**
388 * fdisk_locate_disklabel:
389 * @cxt: context
390 * @n: N item
391 * @name: return item name
392 * @offset: return offset where is item
393 * @size: of the item
394 *
395 * Locate disklabel and returns info about @n item of the label. For example
396 * GPT is composed from two items, PMBR and GPT, n=0 return offset to PMBR and n=1
397 * return offset to GPT. For more details see 'D' expert fdisk command.
398 *
399 * Returns: 0 on success, <0 on error, 1 no more items.
400 */
401 int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name,
402 uint64_t *offset, size_t *size)
403 {
404 if (!cxt || !cxt->label)
405 return -EINVAL;
406 if (!cxt->label->op->locate)
407 return -ENOSYS;
408
409 DBG(CXT, ul_debugobj(cxt, "locating %d chunk of %s.", n, cxt->label->name));
410 return cxt->label->op->locate(cxt, n, name, offset, size);
411 }
412
413
414 /**
415 * fdisk_get_disklabel_id:
416 * @cxt: fdisk context
417 * @id: returns pointer to allocated string (MBR Id or GPT dirk UUID)
418 *
419 * Returns: 0 on success, otherwise, a corresponding error.
420 */
421 int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
422 {
423 struct fdisk_labelitem item = FDISK_LABELITEM_INIT;
424 int rc;
425
426 if (!cxt || !cxt->label || !id)
427 return -EINVAL;
428
429 DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name));
430
431 rc = fdisk_get_disklabel_item(cxt, FDISK_LABELITEM_ID, &item);
432 if (rc == 0) {
433 *id = item.data.str;
434 item.data.str = NULL;
435 }
436 fdisk_reset_labelitem(&item);
437 if (rc > 0)
438 rc = 0;
439 return rc;
440 }
441
442 /**
443 * fdisk_get_disklabel_item:
444 * @cxt: fdisk context
445 * @id: item ID (FDISK_LABELITEM_* or *_LABELITEM_*)
446 * @item: specifies and returns the item
447 *
448 * Note that @id is always in range 0..N. It's fine to use the function in loop
449 * until it returns error or 2, the result in @item should be ignored when
450 * function returns 1. Don't forget to use fdisk_reset_labelitem() or fdisk_unref_labelitem().
451 *
452 * Returns: 0 on success, < 0 on error, 1 on unsupported item, 2 id out of range
453 */
454 int fdisk_get_disklabel_item(struct fdisk_context *cxt, int id, struct fdisk_labelitem *item)
455 {
456 if (!cxt || !cxt->label || !item)
457 return -EINVAL;
458
459 fdisk_reset_labelitem(item);
460 item->id = id;
461 DBG(CXT, ul_debugobj(cxt, "asking for disk %s item %d", cxt->label->name, item->id));
462
463 if (!cxt->label->op->get_item)
464 return -ENOSYS;
465
466 return cxt->label->op->get_item(cxt, item);
467 }
468
469 /**
470 * fdisk_set_disklabel_id:
471 * @cxt: fdisk context
472 *
473 * Returns: 0 on success, otherwise, a corresponding error.
474 */
475 int fdisk_set_disklabel_id(struct fdisk_context *cxt)
476 {
477 if (!cxt || !cxt->label)
478 return -EINVAL;
479 if (!cxt->label->op->set_id)
480 return -ENOSYS;
481
482 DBG(CXT, ul_debugobj(cxt, "setting %s disk ID", cxt->label->name));
483 return cxt->label->op->set_id(cxt);
484 }
485
486 /**
487 * fdisk_set_partition_type:
488 * @cxt: fdisk context
489 * @partnum: partition number
490 * @t: new type
491 *
492 * Returns: 0 on success, < 0 on error.
493 */
494 int fdisk_set_partition_type(struct fdisk_context *cxt,
495 size_t partnum,
496 struct fdisk_parttype *t)
497 {
498 if (!cxt || !cxt->label || !t)
499 return -EINVAL;
500
501
502 if (cxt->label->op->set_part) {
503 struct fdisk_partition *pa = fdisk_new_partition();
504 int rc;
505
506 if (!pa)
507 return -ENOMEM;
508 fdisk_partition_set_type(pa, t);
509
510 DBG(CXT, ul_debugobj(cxt, "partition: %zd: set type", partnum));
511 rc = cxt->label->op->set_part(cxt, partnum, pa);
512 fdisk_unref_partition(pa);
513 return rc;
514 }
515
516 return -ENOSYS;
517 }
518
519
520 /**
521 * fdisk_toggle_partition_flag:
522 * @cxt: fdisk context
523 * @partnum: partition number
524 * @flag: flag ID
525 *
526 * Returns: 0 on success, otherwise, a corresponding error.
527 */
528 int fdisk_toggle_partition_flag(struct fdisk_context *cxt,
529 size_t partnum,
530 unsigned long flag)
531 {
532 int rc;
533
534 if (!cxt || !cxt->label)
535 return -EINVAL;
536 if (!cxt->label->op->part_toggle_flag)
537 return -ENOSYS;
538
539 rc = cxt->label->op->part_toggle_flag(cxt, partnum, flag);
540
541 DBG(CXT, ul_debugobj(cxt, "partition: %zd: toggle: 0x%04lx [rc=%d]", partnum, flag, rc));
542 return rc;
543 }
544
545 /**
546 * fdisk_reorder_partitions
547 * @cxt: fdisk context
548 *
549 * Sort partitions according to the partition start sector.
550 *
551 * Returns: 0 on success, 1 reorder unnecessary, otherwise a corresponding error.
552 */
553 int fdisk_reorder_partitions(struct fdisk_context *cxt)
554 {
555 if (!cxt || !cxt->label)
556 return -EINVAL;
557 if (!cxt->label->op->reorder)
558 return -ENOSYS;
559
560 return cxt->label->op->reorder(cxt);
561 }
562
563 /*
564 * Resets the current used label driver to initial state
565 */
566 void fdisk_deinit_label(struct fdisk_label *lb)
567 {
568 assert(lb);
569
570 /* private label information */
571 if (lb->op->deinit)
572 lb->op->deinit(lb);
573 }
574
575 /**
576 * fdisk_label_set_changed:
577 * @lb: label
578 * @changed: 0/1
579 *
580 * Marks in-memory data as changed, to force fdisk_write_disklabel() to write
581 * to device. This should be unnecessary by default, the library keeps track
582 * about changes.
583 */
584 void fdisk_label_set_changed(struct fdisk_label *lb, int changed)
585 {
586 assert(lb);
587 lb->changed = changed ? 1 : 0;
588 }
589
590 /**
591 * fdisk_label_is_changed:
592 * @lb: label
593 *
594 * Returns: 1 if in-memory data has been changed.
595 */
596 int fdisk_label_is_changed(const struct fdisk_label *lb)
597 {
598 assert(lb);
599 return lb ? lb->changed : 0;
600 }
601
602 /**
603 * fdisk_label_set_disabled:
604 * @lb: label
605 * @disabled: 0 or 1
606 *
607 * Mark label as disabled, then libfdisk is going to ignore the label when
608 * probe device for labels.
609 */
610 void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled)
611 {
612 assert(lb);
613
614 DBG(LABEL, ul_debug("%s label %s",
615 lb->name,
616 disabled ? "DISABLED" : "ENABLED"));
617 lb->disabled = disabled ? 1 : 0;
618 }
619
620 /**
621 * fdisk_label_is_disabled:
622 * @lb: label
623 *
624 * Returns: 1 if label driver disabled.
625 */
626 int fdisk_label_is_disabled(const struct fdisk_label *lb)
627 {
628 assert(lb);
629 return lb ? lb->disabled : 0;
630 }
631
632 /**
633 * fdisk_label_get_geomrange_sectors:
634 * @lb: label
635 * @mi: minimal number
636 * @ma: maximal number
637 *
638 * The function provides minimal and maximal geometry supported for the label,
639 * if no range defined by library than returns -ENOSYS.
640 *
641 * Since: 2.32
642 *
643 * Returns: 0 on success, otherwise, a corresponding error.
644 */
645 int fdisk_label_get_geomrange_sectors(const struct fdisk_label *lb,
646 fdisk_sector_t *mi, fdisk_sector_t *ma)
647 {
648 if (!lb || lb->geom_min.sectors == 0)
649 return -ENOSYS;
650 if (mi)
651 *mi = lb->geom_min.sectors;
652 if (ma)
653 *ma = lb->geom_max.sectors;
654 return 0;
655 }
656
657 /**
658 * fdisk_label_get_geomrange_heads:
659 * @lb: label
660 * @mi: minimal number
661 * @ma: maximal number
662 *
663 * The function provides minimal and maximal geometry supported for the label,
664 * if no range defined by library than returns -ENOSYS.
665 *
666 * Since: 2.32
667 *
668 * Returns: 0 on success, otherwise, a corresponding error.
669 */
670 int fdisk_label_get_geomrange_heads(const struct fdisk_label *lb,
671 unsigned int *mi, unsigned int *ma)
672 {
673 if (!lb || lb->geom_min.heads == 0)
674 return -ENOSYS;
675 if (mi)
676 *mi = lb->geom_min.heads;
677 if (ma)
678 *ma = lb->geom_max.heads;
679 return 0;
680 }
681
682 /**
683 * fdisk_label_get_geomrange_cylinders:
684 * @lb: label
685 * @mi: minimal number
686 * @ma: maximal number
687 *
688 * The function provides minimal and maximal geometry supported for the label,
689 * if no range defined by library than returns -ENOSYS.
690 *
691 * Since: 2.32
692 *
693 * Returns: 0 on success, otherwise, a corresponding error.
694 */
695 int fdisk_label_get_geomrange_cylinders(const struct fdisk_label *lb,
696 fdisk_sector_t *mi, fdisk_sector_t *ma)
697 {
698 if (!lb || lb->geom_min.cylinders == 0)
699 return -ENOSYS;
700 if (mi)
701 *mi = lb->geom_min.cylinders;
702 if (ma)
703 *ma = lb->geom_max.cylinders;
704 return 0;
705 }
706