]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/partition.c
libfdisk: allow to specify partition size by explicit number of sectors
[thirdparty/util-linux.git] / libfdisk / src / partition.c
1
2 #include "c.h"
3 #include "strutils.h"
4
5 #include "fdiskP.h"
6
7 /**
8 * fdisk_new_partition:
9 *
10 * Returns: new instance.
11 */
12 struct fdisk_partition *fdisk_new_partition(void)
13 {
14 struct fdisk_partition *pa = calloc(1, sizeof(*pa));
15
16 pa->refcount = 1;
17 INIT_LIST_HEAD(&pa->parts);
18 pa->partno = FDISK_EMPTY_PARTNO;
19 pa->parent_partno = FDISK_EMPTY_PARTNO;
20 DBG(PART, ul_debugobj(pa, "alloc"));
21 return pa;
22 }
23
24 /**
25 * fdisk_reset_partition:
26 * @pa: partition
27 *
28 * Resets partition content.
29 */
30 void fdisk_reset_partition(struct fdisk_partition *pa)
31 {
32 int ref;
33
34 if (!pa)
35 return;
36
37 DBG(PART, ul_debugobj(pa, "reset"));
38 ref = pa->refcount;
39 fdisk_free_parttype(pa->type);
40 free(pa->name);
41 free(pa->uuid);
42 free(pa->attrs);
43 memset(pa, 0, sizeof(*pa));
44 pa->partno = FDISK_EMPTY_PARTNO;
45 pa->parent_partno = FDISK_EMPTY_PARTNO;
46 pa->refcount = ref;
47 INIT_LIST_HEAD(&pa->parts);
48 }
49
50 /**
51 * fdisk_ref_partition:
52 * @tb: partition pointer
53 *
54 * Incremparts reference counter.
55 */
56 void fdisk_ref_partition(struct fdisk_partition *pa)
57 {
58 if (pa)
59 pa->refcount++;
60 }
61
62 /**
63 * fdisk_unref_partition:
64 * @tb: partition pointer
65 *
66 * De-incremparts reference counter, on zero the @tb is automatically
67 * deallocated.
68 */
69 void fdisk_unref_partition(struct fdisk_partition *pa)
70 {
71 if (!pa)
72 return;
73
74 pa->refcount--;
75 if (pa->refcount <= 0) {
76 fdisk_reset_partition(pa);
77 list_del(&pa->parts);
78 DBG(PART, ul_debugobj(pa, "free"));
79 free(pa);
80 }
81 }
82
83 /**
84 * fdisk_partition_set_start:
85 * @pa: partition
86 * @off: offset in sectors
87 *
88 * Returns: 0 on success, <0 on error.
89 */
90 int fdisk_partition_set_start(struct fdisk_partition *pa, uint64_t off)
91 {
92 if (!pa)
93 return -EINVAL;
94 pa->start = off;
95 return 0;
96 }
97
98 /**
99 * fdisk_partition_get_start:
100 * @pa: partition
101 *
102 * Returns: start offset in sectors
103 */
104 uint64_t fdisk_partition_get_start(struct fdisk_partition *pa)
105 {
106 return pa ? pa->start : 0;
107 }
108
109 /**
110 * fdisk_partition_cmp_start:
111 * @a: partition
112 * @b: partition
113 * See fdisk_sort_table().
114 */
115 int fdisk_partition_cmp_start(struct fdisk_partition *a,
116 struct fdisk_partition *b)
117 {
118 return a->start - b->start;
119 }
120
121 /**
122 * fdisk_partition_set_end:
123 * @pa: partition
124 * @off: offset in sectors
125 *
126 * Sets end offset, and zeroize size.
127 *
128 * The usual way is to address end of the partition by fdisk_partition_set_size().
129 *
130 * Returns: 0 on success, <0 on error.
131 */
132 int fdisk_partition_set_end(struct fdisk_partition *pa, uint64_t off)
133 {
134 if (!pa)
135 return -EINVAL;
136 pa->end = off;
137 pa->size = 0;
138 return 0;
139 }
140
141 /**
142 * fdisk_partition_get_start:
143 * @pa: partition
144 *
145 * Returns: start offset in sectors
146 */
147 uint64_t fdisk_partition_get_end(struct fdisk_partition *pa)
148 {
149 return pa ? pa->end : 0;
150 }
151
152 /**
153 * fdisk_partition_set_size
154 * @pa: partition
155 * @size: in bytes
156 *
157 * Sets size, zeroize end offset. See also fdisk_partition_set_end().
158 *
159 * Returns: 0 on success, <0 on error.
160 */
161 int fdisk_partition_set_size(struct fdisk_partition *pa, uint64_t size)
162 {
163 if (!pa)
164 return -EINVAL;
165 pa->size = size;
166 pa->end = 0;
167 return 0;
168 }
169
170 /**
171 * fdisk_partition_get_start:
172 * @pa: partition
173 *
174 * Returns: size in sectors
175 */
176 uint64_t fdisk_partition_get_size(struct fdisk_partition *pa)
177 {
178 return pa ? pa->size : 0;
179 }
180
181 /**
182 * fdisk_partition_set_partno
183 * @pa: partition
184 * @n: partitiion number
185 *
186 * When @pa used as a tempalate for fdisk_add_partition() when infor label driver
187 * about wanted partition position.
188 *
189 * Returns: 0 on success, <0 on error.
190 */
191 int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t n)
192 {
193 if (!pa)
194 return -EINVAL;
195 pa->partno = n;
196 return 0;
197 }
198
199 size_t fdisk_partition_get_partno(struct fdisk_partition *pa)
200 {
201 return pa ? pa->partno : (size_t) -1;
202 }
203
204 int fdisk_partition_cmp_partno(struct fdisk_partition *a,
205 struct fdisk_partition *b)
206 {
207 return a->partno - b->partno;
208 }
209
210 int fdisk_partition_set_type(struct fdisk_partition *pa,
211 const struct fdisk_parttype *type)
212 {
213 if (!pa)
214 return -EINVAL;
215 fdisk_free_parttype(pa->type);
216 pa->type = fdisk_copy_parttype(type);
217 return 0;
218 }
219
220 const struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa)
221 {
222 return pa ? pa->type : NULL;
223 }
224
225 int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name)
226 {
227 char *p = NULL;
228
229 if (!pa)
230 return -EINVAL;
231 if (name) {
232 p = strdup(name);
233 if (!p)
234 return -ENOMEM;
235 }
236 free(pa->name);
237 pa->name = p;
238 return 0;
239 }
240
241 const char *fdisk_partition_get_name(struct fdisk_partition *pa)
242 {
243 return pa ? pa->name : NULL;
244 }
245
246 int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid)
247 {
248 char *p = NULL;
249
250 if (!pa)
251 return -EINVAL;
252 if (uuid) {
253 p = strdup(uuid);
254 if (!p)
255 return -ENOMEM;
256 }
257 free(pa->uuid);
258 pa->uuid = p;
259 return 0;
260 }
261
262 /**
263 * fdisk_partition_partno_follow_default
264 * @pa: partition
265 * @enable: 0|1
266 *
267 * When @pa used as a tempalate for fdisk_add_partition() when force label driver
268 * to add a new partition to the default (next) position.
269 *
270 * Returns: 0 on success, <0 on error.
271 */
272 int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable)
273 {
274 if (!pa)
275 return -EINVAL;
276 pa->partno_follow_default = enable ? 1 : 0;
277 return 0;
278 }
279
280 /**
281 * fdisk_partition_start_follow_default
282 * @pa: partition
283 * @enable: 0|1
284 *
285 * When @pa used as a tempalate for fdisk_add_partition() when force label driver
286 * to use the first possible space for the new partition.
287 *
288 * Returns: 0 on success, <0 on error.
289 */
290 int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable)
291 {
292 if (!pa)
293 return -EINVAL;
294 pa->start_follow_default = enable ? 1 : 0;
295 return 0;
296 }
297
298 /**
299 * fdisk_partition_start_is_default:
300 * @pa: partition
301 *
302 * Returns: 1 if the partition follows default
303 */
304 int fdisk_partition_start_is_default(struct fdisk_partition *pa)
305 {
306 assert(pa);
307 return pa->start_follow_default;
308 }
309
310 /**
311 * fdisk_partition_start_follow_default
312 * @pa: partition
313 * @enable: 0|1
314 *
315 * When @pa used as a tempalate for fdisk_add_partition() when force label driver
316 * to use all the possible space for the new partition.
317 *
318 * Returns: 0 on success, <0 on error.
319 */
320 int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable)
321 {
322 if (!pa)
323 return -EINVAL;
324 pa->end_follow_default = enable ? 1 : 0;
325 return 0;
326 }
327
328 /**
329 * fdisk_partition_end_is_default:
330 * @pa: partition
331 *
332 * Returns: 1 if the partition follows default
333 */
334 int fdisk_partition_end_is_default(struct fdisk_partition *pa)
335 {
336 assert(pa);
337 return pa->end_follow_default;
338 }
339
340 /**
341 * fdisk_partition_size_explicit:
342 * @pa: partition
343 * @enable: 0|1
344 *
345 * By default libfdisk aligns the size when add the new partition (by
346 * fdisk_add_partrition()). If you want to disable this functionality use
347 * @enable = 1.
348 *
349 * Returns: 0 on success, <0 on error.
350 */
351 int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable)
352 {
353 if (!pa)
354 return -EINVAL;
355 pa->size_explicit = enable ? 1 : 0;
356 return 0;
357 }
358
359 const char *fdisk_partition_get_uuid(struct fdisk_partition *pa)
360 {
361 return pa ? pa->uuid : NULL;
362 }
363
364 const char *fdisk_partition_get_attrs(struct fdisk_partition *pa)
365 {
366 return pa ? pa->attrs : NULL;
367 }
368
369 int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs)
370 {
371 char *p = NULL;
372
373 if (!pa)
374 return -EINVAL;
375 if (attrs) {
376 p = strdup(attrs);
377 if (!p)
378 return -ENOMEM;
379 }
380 free(pa->attrs);
381 pa->attrs = p;
382 return 0;
383 }
384
385 int fdisk_partition_is_nested(struct fdisk_partition *pa)
386 {
387 return pa && pa->parent_partno != FDISK_EMPTY_PARTNO;
388 }
389
390 int fdisk_partition_is_container(struct fdisk_partition *pa)
391 {
392 return pa && pa->container;
393 }
394
395 int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
396 {
397 if (pa && parent)
398 *parent = pa->parent_partno;
399 else
400 return -EINVAL;
401 return 0;
402 }
403
404 int fdisk_partition_is_used(struct fdisk_partition *pa)
405 {
406 return pa && pa->used;
407 }
408
409 int fdisk_partition_is_bootable(struct fdisk_partition *pa)
410 {
411 return pa && pa->boot;
412 }
413
414 int fdisk_partition_is_freespace(struct fdisk_partition *pa)
415 {
416 return pa && pa->freespace;
417 }
418
419 int fdisk_partition_next_partno(
420 struct fdisk_partition *pa,
421 struct fdisk_context *cxt,
422 size_t *n)
423 {
424 assert(cxt);
425 assert(n);
426
427 if (pa && pa->partno_follow_default) {
428 size_t i;
429
430 DBG(PART, ul_debugobj(pa, "next partno (follow default)"));
431
432 for (i = 0; i < cxt->label->nparts_max; i++) {
433 if (!fdisk_is_partition_used(cxt, i)) {
434 *n = i;
435 return 0;
436 }
437 }
438 return -ERANGE;
439
440 } else if (pa && pa->partno != FDISK_EMPTY_PARTNO) {
441
442 DBG(PART, ul_debugobj(pa, "next partno (specified=%zu)", pa->partno));
443
444 if (pa->partno >= cxt->label->nparts_max)
445 return -ERANGE;
446 *n = pa->partno;
447 } else
448 return fdisk_ask_partnum(cxt, n, 1);
449
450 return 0;
451 }
452
453 /**
454 * fdisk_partition_to_string:
455 * @pa: partition
456 * @id: field (FDISK_FIELD_*)
457 * @data: returns string with allocated data
458 *
459 * Returns info about partition converted to printable string.
460 *
461 * For example
462 *
463 * struct fdisk_parition *pa;
464 *
465 * fdisk_get_partition(cxt, 0, &pa);
466 * fdisk_partition_to_string(pa, FDISK_FIELD_UUID, &data);
467 * printf("first partition uuid: %s\n", data);
468 * free(data);
469 * fdisk_unref_partition(pa);
470 *
471 * returns UUID for the first partition.
472 *
473 * Returns 0 on success, otherwise, a corresponding error.
474 */
475
476 int fdisk_partition_to_string(struct fdisk_partition *pa,
477 struct fdisk_context *cxt,
478 int id,
479 char **data)
480 {
481 char *p = NULL;
482 int rc = 0;
483 uint64_t x;
484
485 if (!pa || !cxt)
486 return -EINVAL;
487
488 switch (id) {
489 case FDISK_FIELD_DEVICE:
490 if (pa->freespace)
491 p = strdup(_("Free space"));
492 else if (cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
493 rc = asprintf(&p, "%c", (int) pa->partno + 'a');
494 else
495 p = fdisk_partname(cxt->dev_path, pa->partno + 1);
496 break;
497 case FDISK_FIELD_BOOT:
498 rc = asprintf(&p, "%c", pa->boot ? '*' : ' ');
499 break;
500 case FDISK_FIELD_START:
501 x = fdisk_cround(cxt, pa->start);
502 rc = pa->start_post ?
503 asprintf(&p, "%ju%c", x, pa->start_post) :
504 asprintf(&p, "%ju", x);
505 break;
506 case FDISK_FIELD_END:
507 x = fdisk_cround(cxt, pa->end);
508 rc = pa->end_post ?
509 asprintf(&p, "%ju%c", x, pa->end_post) :
510 asprintf(&p, "%ju", x);
511 break;
512 case FDISK_FIELD_SIZE:
513 {
514 uint64_t sz = pa->size * cxt->sector_size;
515
516 if (fdisk_is_details(cxt)) {
517 rc = pa->size_post ?
518 asprintf(&p, "%ju%c", sz, pa->size_post) :
519 asprintf(&p, "%ju", sz);
520 } else {
521 p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
522 if (!p)
523 rc = -ENOMEM;
524 }
525 break;
526 }
527 case FDISK_FIELD_CYLINDERS:
528 rc = asprintf(&p, "%ju", (uintmax_t)
529 fdisk_cround(cxt, pa->size));
530 break;
531 case FDISK_FIELD_SECTORS:
532 rc = asprintf(&p, "%ju", pa->size);
533 break;
534 case FDISK_FIELD_BSIZE:
535 rc = asprintf(&p, "%ju", pa->bsize);
536 break;
537 case FDISK_FIELD_FSIZE:
538 rc = asprintf(&p, "%ju", pa->fsize);
539 break;
540 case FDISK_FIELD_CPG:
541 rc = asprintf(&p, "%ju", pa->cpg);
542 break;
543 case FDISK_FIELD_TYPE:
544 p = pa->type && pa->type->name ? strdup(pa->type->name) : NULL;
545 break;
546 case FDISK_FIELD_TYPEID:
547 if (pa->type && fdisk_parttype_get_string(pa->type))
548 rc = asprintf(&p, "%s", fdisk_parttype_get_string(pa->type));
549 else if (pa->type)
550 rc = asprintf(&p, "%x", fdisk_parttype_get_code(pa->type));
551 break;
552 case FDISK_FIELD_UUID:
553 p = pa->uuid ? strdup(pa->uuid) : NULL;
554 break;
555 case FDISK_FIELD_NAME:
556 p = pa->name ? strdup(pa->name) : NULL;
557 break;
558 case FDISK_FIELD_ATTR:
559 p = pa->attrs ? strdup(pa->attrs) : NULL;
560 break;
561 case FDISK_FIELD_SADDR:
562 p = pa->start_addr ? strdup(pa->start_addr) : NULL;
563 break;
564 case FDISK_FIELD_EADDR:
565 p = pa->end_addr ? strdup(pa->end_addr) : NULL;
566 break;
567 default:
568 return -EINVAL;
569 }
570
571 if (rc < 0)
572 rc = -ENOMEM;
573 else if (rc > 0)
574 rc = 0;
575
576 if (data)
577 *data = p;
578 return rc;
579 }
580
581 /**
582 * fdisk_get_partition:
583 * @cxt: context
584 * @partno: partition nuymber
585 * @pa: returns data about partition
586 *
587 * Fills in @pa with data about partition @n. Note that partno may address
588 * unused partition and then this function does not fill anything to @pa.
589 * See fdisk_is_partition_used(). If @pa points to NULL then the function
590 * allocates a newly allocated fdisk_partition struct.
591 *
592 * Returns: 0 on success, otherwise, a corresponding error.
593 */
594 int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
595 struct fdisk_partition **pa)
596 {
597 int rc;
598 struct fdisk_partition *np = NULL;
599
600 if (!cxt || !cxt->label || !pa)
601 return -EINVAL;
602 if (!cxt->label->op->get_part)
603 return -ENOSYS;
604 if (!fdisk_is_partition_used(cxt, partno))
605 return -EINVAL;
606
607 if (!*pa) {
608 np = *pa = fdisk_new_partition();
609 if (!*pa)
610 return -ENOMEM;
611 } else
612 fdisk_reset_partition(*pa);
613
614 (*pa)->partno = partno;
615 rc = cxt->label->op->get_part(cxt, partno, *pa);
616
617 if (rc) {
618 if (np) {
619 fdisk_unref_partition(np);
620 *pa = NULL;
621 } else
622 fdisk_reset_partition(*pa);
623 } else
624 (*pa)->size_explicit = 1;
625 return rc;
626 }
627
628 /**
629 * fdisk_set_partition:
630 * @cxt: context
631 * @partno: partition nuymber
632 * @pa: new partition setting
633 *
634 * Returns: 0 on success, <0 on error.
635 */
636 int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
637 struct fdisk_partition *pa)
638 {
639 if (!cxt || !cxt->label || !pa)
640 return -EINVAL;
641 if (!cxt->label->op->set_part)
642 return -ENOSYS;
643
644 DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju, "
645 "defaults(start=%s, end=%s, partno=%s)",
646 partno, pa,
647 pa->start,
648 pa->end,
649 pa->size,
650 pa->start_follow_default ? "yes" : "no",
651 pa->end_follow_default ? "yes" : "no",
652 pa->partno_follow_default ? "yes" : "no"));
653
654 return cxt->label->op->set_part(cxt, partno, pa);
655 }
656
657 /**
658 * fdisk_add_partition:
659 * @cxt: fdisk context
660 * @pa: template for the partition (or NULL)
661 * @partno: NULL or returns new partition number
662 *
663 * If @pa is not specified or any @pa item is missiong the libfdisk will ask by
664 * fdisk_ask_ API.
665 *
666 * Creates a new partition.
667 *
668 * Returns: 0 on success, <0 on error.
669 */
670 int fdisk_add_partition(struct fdisk_context *cxt,
671 struct fdisk_partition *pa,
672 size_t *partno)
673 {
674 int rc;
675
676 assert(cxt);
677 assert(cxt->label);
678
679 if (!cxt || !cxt->label)
680 return -EINVAL;
681 if (!cxt->label->op->add_part)
682 return -ENOSYS;
683 if (fdisk_missing_geometry(cxt))
684 return -EINVAL;
685
686 if (pa)
687 DBG(CXT, ul_debugobj(cxt, "adding new partition %p (start=%ju, end=%ju, size=%ju, "
688 "defaults(start=%s, end=%s, partno=%s)",
689 pa,
690 pa->start,
691 pa->end,
692 pa->size,
693 pa->start_follow_default ? "yes" : "no",
694 pa->end_follow_default ? "yes" : "no",
695 pa->partno_follow_default ? "yes" : "no"));
696 else
697 DBG(CXT, ul_debugobj(cxt, "adding partition"));
698
699 rc = cxt->label->op->add_part(cxt, pa, partno);
700
701 DBG(CXT, ul_debugobj(cxt, "add partition done (rc=%d)", rc));
702 return rc;
703 }
704
705 /**
706 * fdisk_delete_partition:
707 * @cxt: fdisk context
708 * @partno: partition number to delete
709 *
710 * Deletes a @partno partition.
711 *
712 * Returns: 0 on success, <0 on error
713 */
714 int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
715 {
716 if (!cxt || !cxt->label)
717 return -EINVAL;
718 if (!cxt->label->op->del_part)
719 return -ENOSYS;
720
721 DBG(CXT, ul_debugobj(cxt, "deleting %s partition number %zd",
722 cxt->label->name, partno));
723 return cxt->label->op->del_part(cxt, partno);
724 }
725
726 /**
727 * fdisk_delete_all_partitions:
728 * @cxt: fdisk context
729 *
730 * Delete all used partitions.
731 *
732 * Returns: 0 on success, otherwise, a corresponding error.
733 */
734 int fdisk_delete_all_partitions(struct fdisk_context *cxt)
735 {
736 size_t i;
737 int rc;
738
739 if (!cxt || !cxt->label)
740 return -EINVAL;
741
742 for (i = 0; i < cxt->label->nparts_max; i++) {
743
744 if (!fdisk_is_partition_used(cxt, i))
745 continue;
746 rc = fdisk_delete_partition(cxt, i);
747 if (rc)
748 break;
749 }
750
751 return rc;
752 }
753
754 /*
755 * This is faster than fdisk_get_partition() + fdisk_partition_is_used()
756 */
757 int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n)
758 {
759 if (!cxt || !cxt->label)
760 return -EINVAL;
761 if (!cxt->label->op->part_is_used)
762 return -ENOSYS;
763
764 return cxt->label->op->part_is_used(cxt, n);
765 }
766