]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/partition.c
9a5614d2154a7c756baa74a1c4f46e95e2887047
[thirdparty/util-linux.git] / libfdisk / src / partition.c
1
2 #include "c.h"
3 #include "strutils.h"
4
5 #ifdef HAVE_LIBBLKID
6 # include <blkid.h>
7 #endif
8
9 #include "fdiskP.h"
10
11 /**
12 * SECTION: partition
13 * @title: Partition
14 * @short_description: generic label independent partition abstraction
15 *
16 * The fdisk_partition provides label independent abstraction. The partitions
17 * are not directly connected with partition table (label) data. Any change to
18 * fdisk_partition does not affects in-memory or on-disk label data.
19 *
20 * The fdisk_partition is possible to use as a template for
21 * fdisk_add_partition() or fdisk_set_partition() operations.
22 */
23
24 static void init_partition(struct fdisk_partition *pa)
25 {
26 FDISK_INIT_UNDEF(pa->size);
27 FDISK_INIT_UNDEF(pa->start);
28 FDISK_INIT_UNDEF(pa->partno);
29 FDISK_INIT_UNDEF(pa->parent_partno);
30 FDISK_INIT_UNDEF(pa->boot);
31
32 INIT_LIST_HEAD(&pa->parts);
33 }
34
35 /**
36 * fdisk_new_partition:
37 *
38 * Returns: new instance.
39 */
40 struct fdisk_partition *fdisk_new_partition(void)
41 {
42 struct fdisk_partition *pa = calloc(1, sizeof(*pa));
43 if (!pa)
44 return NULL;
45
46 pa->refcount = 1;
47 init_partition(pa);
48 DBG(PART, ul_debugobj(pa, "alloc"));
49 return pa;
50 }
51
52 /**
53 * fdisk_reset_partition:
54 * @pa: partition
55 *
56 * Resets partition content.
57 */
58 void fdisk_reset_partition(struct fdisk_partition *pa)
59 {
60 int ref;
61
62 if (!pa)
63 return;
64
65 DBG(PART, ul_debugobj(pa, "reset"));
66 ref = pa->refcount;
67
68 fdisk_unref_parttype(pa->type);
69 free(pa->name);
70 free(pa->uuid);
71 free(pa->attrs);
72 free(pa->fstype);
73 free(pa->fsuuid);
74 free(pa->fslabel);
75 free(pa->start_chs);
76 free(pa->end_chs);
77
78 memset(pa, 0, sizeof(*pa));
79 pa->refcount = ref;
80
81 init_partition(pa);
82 }
83
84 static struct fdisk_partition *__copy_partition(struct fdisk_partition *o)
85 {
86 struct fdisk_partition *n = fdisk_new_partition();
87 int rc;
88
89 if (!n)
90 return NULL;
91
92 memcpy(n, o, sizeof(*n));
93
94 /* do not copy reference to lists, etc.*/
95 n->refcount = 1;
96 INIT_LIST_HEAD(&n->parts);
97
98 if (n->type)
99 fdisk_ref_parttype(n->type);
100
101 /* note that strdup_between_structs() deallocates destination pointer,
102 * so make sure it's NULL as we call memcpy() before ... */
103 n->name = NULL;
104 rc = strdup_between_structs(n, o, name);
105
106 n->uuid = NULL;
107 if (!rc)
108 rc = strdup_between_structs(n, o, uuid);
109 n->attrs = NULL;
110 if (!rc)
111 rc = strdup_between_structs(n, o, attrs);
112 n->fstype = NULL;
113 if (!rc)
114 rc = strdup_between_structs(n, o, fstype);
115 n->fsuuid = NULL;
116 if (!rc)
117 rc = strdup_between_structs(n, o, fsuuid);
118 n->fslabel = NULL;
119 if (!rc)
120 rc = strdup_between_structs(n, o, fslabel);
121 n->start_chs = NULL;
122 if (!rc)
123 rc = strdup_between_structs(n, o, start_chs);
124 n->end_chs = NULL;
125 if (!rc)
126 rc = strdup_between_structs(n, o, end_chs);
127
128 if (rc) {
129 fdisk_unref_partition(n);
130 n = NULL;
131 }
132 return n;
133 }
134
135 /**
136 * fdisk_ref_partition:
137 * @pa: partition pointer
138 *
139 * Increments reference counter.
140 */
141 void fdisk_ref_partition(struct fdisk_partition *pa)
142 {
143 if (pa)
144 pa->refcount++;
145 }
146
147 /**
148 * fdisk_unref_partition:
149 * @pa: partition pointer
150 *
151 * Decrements reference counter, on zero the @pa is automatically
152 * deallocated.
153 */
154 void fdisk_unref_partition(struct fdisk_partition *pa)
155 {
156 if (!pa)
157 return;
158
159 pa->refcount--;
160 if (pa->refcount <= 0) {
161 list_del(&pa->parts);
162 fdisk_reset_partition(pa);
163 DBG(PART, ul_debugobj(pa, "free"));
164 free(pa);
165 }
166 }
167
168 /**
169 * fdisk_partition_set_start:
170 * @pa: partition
171 * @off: offset in sectors, maximal is UINT64_MAX-1
172 *
173 * Note that zero is valid offset too. Use fdisk_partition_unset_start() to
174 * undefine the offset.
175 *
176 * Returns: 0 on success, <0 on error.
177 */
178 int fdisk_partition_set_start(struct fdisk_partition *pa, fdisk_sector_t off)
179 {
180 if (!pa)
181 return -EINVAL;
182 if (FDISK_IS_UNDEF(off))
183 return -ERANGE;
184 pa->start = off;
185 pa->fs_probed = 0;
186 return 0;
187 }
188
189 /**
190 * fdisk_partition_unset_start:
191 * @pa: partition
192 *
193 * Sets the size as undefined. See fdisk_partition_has_start().
194 *
195 * Returns: 0 on success, <0 on error.
196 */
197 int fdisk_partition_unset_start(struct fdisk_partition *pa)
198 {
199 if (!pa)
200 return -EINVAL;
201 FDISK_INIT_UNDEF(pa->start);
202 pa->fs_probed = 0;
203 return 0;
204 }
205
206 /**
207 * fdisk_partition_get_start:
208 * @pa: partition
209 *
210 * The zero is also valid offset. The function may return random undefined
211 * value when start offset is undefined (for example after
212 * fdisk_partition_unset_start()). Always use fdisk_partition_has_start() to be
213 * sure that you work with valid numbers.
214 *
215 * Returns: start offset in sectors
216 */
217 fdisk_sector_t fdisk_partition_get_start(struct fdisk_partition *pa)
218 {
219 return pa->start;
220 }
221
222 /**
223 * fdisk_partition_has_start:
224 * @pa: partition
225 *
226 * Returns: 1 or 0
227 */
228 int fdisk_partition_has_start(struct fdisk_partition *pa)
229 {
230 return pa && !FDISK_IS_UNDEF(pa->start);
231 }
232
233
234 /**
235 * fdisk_partition_cmp_start:
236 * @a: partition
237 * @b: partition
238 *
239 * Compares partitions according to start offset, See fdisk_table_sort_partitions().
240 *
241 * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
242 */
243 int fdisk_partition_cmp_start(struct fdisk_partition *a,
244 struct fdisk_partition *b)
245 {
246 int no_a = FDISK_IS_UNDEF(a->start),
247 no_b = FDISK_IS_UNDEF(b->start);
248
249 if (no_a && no_b)
250 return 0;
251 if (no_a)
252 return -1;
253 if (no_b)
254 return 1;
255
256 return cmp_numbers(a->start, b->start);
257 }
258
259 /**
260 * fdisk_partition_start_follow_default
261 * @pa: partition
262 * @enable: 0|1
263 *
264 * When @pa is used as a template for fdisk_add_partition(), it follows the driver's default for
265 * the beginning of the partition. For DOS, it is the first usable space, while for GPT, it is
266 * the first sector of the largest area.
267 *
268 * Returns: 0 on success, <0 on error.
269 */
270 int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable)
271 {
272 if (!pa)
273 return -EINVAL;
274 pa->start_follow_default = enable ? 1 : 0;
275 return 0;
276 }
277
278 /**
279 * fdisk_partition_start_is_default:
280 * @pa: partition
281 *
282 * See fdisk_partition_start_follow_default().
283 *
284 * Returns: 1 if the partition follows default
285 */
286 int fdisk_partition_start_is_default(struct fdisk_partition *pa)
287 {
288 assert(pa);
289 return pa->start_follow_default;
290 }
291
292 /**
293 * fdisk_partition_set_size:
294 * @pa: partition
295 * @sz: size in sectors, maximal is UIN64_MAX-1
296 *
297 * Note that zero is valid size too. Use fdisk_partition_unset_size() to
298 * undefine the size.
299 *
300 * Returns: 0 on success, <0 on error.
301 */
302 int fdisk_partition_set_size(struct fdisk_partition *pa, fdisk_sector_t sz)
303 {
304 if (!pa)
305 return -EINVAL;
306 if (FDISK_IS_UNDEF(sz))
307 return -ERANGE;
308 pa->size = sz;
309 pa->fs_probed = 0;
310 return 0;
311 }
312
313 /**
314 * fdisk_partition_unset_size:
315 * @pa: partition
316 *
317 * Sets the size as undefined. See fdisk_partition_has_size().
318 *
319 * Returns: 0 on success, <0 on error.
320 */
321 int fdisk_partition_unset_size(struct fdisk_partition *pa)
322 {
323 if (!pa)
324 return -EINVAL;
325 FDISK_INIT_UNDEF(pa->size);
326 pa->fs_probed = 0;
327 return 0;
328 }
329
330 /**
331 * fdisk_partition_get_size:
332 * @pa: partition
333 *
334 * The zero is also valid size. The function may return random undefined
335 * value when size is undefined (for example after fdisk_partition_unset_size()).
336 * Always use fdisk_partition_has_size() to be sure that you work with valid
337 * numbers.
338 *
339 * Returns: size offset in sectors
340 */
341 fdisk_sector_t fdisk_partition_get_size(struct fdisk_partition *pa)
342 {
343 return pa->size;
344 }
345
346 /**
347 * fdisk_partition_has_size:
348 * @pa: partition
349 *
350 * Returns: 1 or 0
351 */
352 int fdisk_partition_has_size(struct fdisk_partition *pa)
353 {
354 return pa && !FDISK_IS_UNDEF(pa->size);
355 }
356
357 /**
358 * fdisk_partition_size_explicit:
359 * @pa: partition
360 * @enable: 0|1
361 *
362 * By default libfdisk aligns the size when add the new partition (by
363 * fdisk_add_partition()). If you want to disable this functionality use
364 * @enable = 1.
365 *
366 * Returns: 0 on success, <0 on error.
367 */
368 int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable)
369 {
370 if (!pa)
371 return -EINVAL;
372 pa->size_explicit = enable ? 1 : 0;
373 return 0;
374 }
375
376 /**
377 * fdisk_partition_set_partno:
378 * @pa: partition
379 * @num: partition number (0 is the first partition, maximal is SIZE_MAX-1)
380 *
381 * Note that zero is valid partno too. Use fdisk_partition_unset_partno() to
382 * undefine the partno.
383 *
384 * Returns: 0 on success, <0 on error.
385 */
386 int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num)
387 {
388 if (!pa)
389 return -EINVAL;
390 if (FDISK_IS_UNDEF(num))
391 return -ERANGE;
392 pa->partno = num;
393 return 0;
394 }
395
396 /**
397 * fdisk_partition_unset_partno:
398 * @pa: partition
399 *
400 * Sets the partno as undefined. See fdisk_partition_has_partno().
401 *
402 * Returns: 0 on success, <0 on error.
403 */
404 int fdisk_partition_unset_partno(struct fdisk_partition *pa)
405 {
406 if (!pa)
407 return -EINVAL;
408 FDISK_INIT_UNDEF(pa->partno);
409 return 0;
410 }
411
412 /**
413 * fdisk_partition_get_partno:
414 * @pa: partition
415 *
416 * The zero is also valid partition number. The function may return random
417 * value when partno is undefined (for example after fdisk_partition_unset_partno()).
418 * Always use fdisk_partition_has_partno() to be sure that you work with valid
419 * numbers.
420 *
421 * Returns: partition number (0 is the first partition)
422 */
423 size_t fdisk_partition_get_partno(struct fdisk_partition *pa)
424 {
425 return pa->partno;
426 }
427
428 /**
429 * fdisk_partition_has_partno:
430 * @pa: partition
431 *
432 * Returns: 1 or 0
433 */
434 int fdisk_partition_has_partno(struct fdisk_partition *pa)
435 {
436 return pa && !FDISK_IS_UNDEF(pa->partno);
437 }
438
439
440 /**
441 * fdisk_partition_cmp_partno:
442 * @a: partition
443 * @b: partition
444 *
445 * Compares partitions according to partition number See fdisk_table_sort_partitions().
446 *
447 * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
448 */
449 int fdisk_partition_cmp_partno(struct fdisk_partition *a,
450 struct fdisk_partition *b)
451 {
452 return a->partno - b->partno;
453 }
454
455 /**
456 * fdisk_partition_partno_follow_default
457 * @pa: partition
458 * @enable: 0|1
459 *
460 * When @pa used as a template for fdisk_add_partition() when force label driver
461 * to add a new partition to the default (next) position.
462 *
463 * Returns: 0 on success, <0 on error.
464 */
465 int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable)
466 {
467 if (!pa)
468 return -EINVAL;
469 pa->partno_follow_default = enable ? 1 : 0;
470 return 0;
471 }
472
473 /**
474 * fdisk_partition_set_type:
475 * @pa: partition
476 * @type: partition type
477 *
478 * Sets partition type.
479 *
480 * Returns: 0 on success, <0 on error.
481 */
482 int fdisk_partition_set_type(struct fdisk_partition *pa,
483 struct fdisk_parttype *type)
484 {
485 if (!pa)
486 return -EINVAL;
487
488 fdisk_ref_parttype(type);
489 fdisk_unref_parttype(pa->type);
490 pa->type = type;
491
492 return 0;
493 }
494
495 /**
496 * fdisk_partition_get_type:
497 * @pa: partition
498 *
499 * Returns: pointer to partition type.
500 */
501 struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa)
502 {
503 return pa ? pa->type : NULL;
504 }
505
506 /**
507 * fdisk_partition_set_name:
508 * @pa: partition
509 * @name: partition name
510 *
511 * Returns: 0 on success, <0 on error.
512 */
513 int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name)
514 {
515 if (!pa)
516 return -EINVAL;
517 return strdup_to_struct_member(pa, name, name);
518 }
519
520 /**
521 * fdisk_partition_get_name:
522 * @pa: partition
523 *
524 * Returns: partition name
525 */
526 const char *fdisk_partition_get_name(struct fdisk_partition *pa)
527 {
528 return pa ? pa->name : NULL;
529 }
530
531 /**
532 * fdisk_partition_set_uuid:
533 * @pa: partition
534 * @uuid: UUID of the partition
535 *
536 * Returns: 0 on success, <0 on error.
537 */
538 int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid)
539 {
540 if (!pa)
541 return -EINVAL;
542 return strdup_to_struct_member(pa, uuid, uuid);
543 }
544
545 /**
546 * fdisk_partition_has_end:
547 * @pa: partition
548 *
549 * Returns: 1 if the partition has defined last sector
550 */
551 int fdisk_partition_has_end(struct fdisk_partition *pa)
552 {
553 return pa && !FDISK_IS_UNDEF(pa->start) && !FDISK_IS_UNDEF(pa->size);
554 }
555
556 /**
557 * fdisk_partition_get_end:
558 * @pa: partition
559 *
560 * This function may returns absolute non-sense, always check
561 * fdisk_partition_has_end().
562 *
563 * Note that partition end is defined by fdisk_partition_set_start() and
564 * fdisk_partition_set_size().
565 *
566 * Returns: last partition sector LBA.
567 */
568 fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa)
569 {
570 return pa->start + pa->size - (pa->size == 0 ? 0 : 1);
571 }
572
573 /**
574 * fdisk_partition_end_follow_default
575 * @pa: partition
576 * @enable: 0|1
577 *
578 * When @pa used as a template for fdisk_add_partition() when force label
579 * driver to use all the possible space for the new partition.
580 *
581 * Returns: 0 on success, <0 on error.
582 */
583 int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable)
584 {
585 if (!pa)
586 return -EINVAL;
587 pa->end_follow_default = enable ? 1 : 0;
588 return 0;
589 }
590
591 /**
592 * fdisk_partition_end_is_default:
593 * @pa: partition
594 *
595 * Returns: 1 if the partition follows default
596 */
597 int fdisk_partition_end_is_default(struct fdisk_partition *pa)
598 {
599 assert(pa);
600 return pa->end_follow_default;
601 }
602
603 /**
604 * fdisk_partition_get_uuid:
605 * @pa: partition
606 *
607 * Returns: partition UUID as string
608 */
609 const char *fdisk_partition_get_uuid(struct fdisk_partition *pa)
610 {
611 return pa ? pa->uuid : NULL;
612 }
613
614 /**
615 * fdisk_partition_get_attrs:
616 * @pa: partition
617 *
618 * Returns: partition attributes in string format
619 */
620 const char *fdisk_partition_get_attrs(struct fdisk_partition *pa)
621 {
622 return pa ? pa->attrs : NULL;
623 }
624
625 /**
626 * fdisk_partition_set_attrs:
627 * @pa: partition
628 * @attrs: attributes
629 *
630 * Sets @attrs to @pa.
631 *
632 * Return: 0 on success, <0 on error.
633 */
634 int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs)
635 {
636 if (!pa)
637 return -EINVAL;
638 return strdup_to_struct_member(pa, attrs, attrs);
639 }
640
641 /**
642 * fdisk_partition_is_nested:
643 * @pa: partition
644 *
645 * Returns: 1 if the partition is nested (e.g. MBR logical partition)
646 */
647 int fdisk_partition_is_nested(struct fdisk_partition *pa)
648 {
649 return pa && !FDISK_IS_UNDEF(pa->parent_partno);
650 }
651
652 /**
653 * fdisk_partition_is_container:
654 * @pa: partition
655 *
656 * Returns: 1 if the partition is container (e.g. MBR extended partition)
657 */
658 int fdisk_partition_is_container(struct fdisk_partition *pa)
659 {
660 return pa && pa->container;
661 }
662
663 /**
664 * fdisk_partition_get_parent:
665 * @pa: partition
666 * @parent: parent parno
667 *
668 * Returns: returns devno of the parent
669 */
670 int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
671 {
672 if (pa && parent)
673 *parent = pa->parent_partno;
674 else
675 return -EINVAL;
676 return 0;
677 }
678
679 /**
680 * fdisk_partition_is_used:
681 * @pa: partition
682 *
683 * Returns: 1 if the partition points to some area
684 */
685 int fdisk_partition_is_used(struct fdisk_partition *pa)
686 {
687 return pa && pa->used;
688 }
689
690 /**
691 * fdisk_partition_is_bootable:
692 * @pa: partition
693 *
694 * Returns: 1 if the partition has enabled boot flag
695 */
696 int fdisk_partition_is_bootable(struct fdisk_partition *pa)
697 {
698 return pa && pa->boot == 1;
699 }
700
701 /**
702 * fdisk_partition_is_freespace:
703 * @pa: partition
704 *
705 * Returns: 1 if @pa points to freespace
706 */
707 int fdisk_partition_is_freespace(struct fdisk_partition *pa)
708 {
709 return pa && pa->freespace;
710 }
711
712 /**
713 * fdisk_partition_is_wholedisk:
714 * @pa: partition
715 *
716 * Returns: 1 if the partition is special whole-disk (e.g. SUN) partition
717 */
718 int fdisk_partition_is_wholedisk(struct fdisk_partition *pa)
719 {
720 return pa && pa->wholedisk;
721 }
722
723 /**
724 * fdisk_partition_next_partno:
725 * @pa: partition
726 * @cxt: context
727 * @n: returns partition number
728 *
729 * If @pa specified and partno-follow-default (see fdisk_partition_partno_follow_default())
730 * enabled then returns next expected partno or -ERANGE on error.
731 *
732 * If @pa is NULL, or @pa does not specify any semantic for the next partno
733 * then use Ask API to ask user for the next partno. In this case returns 1 if
734 * no free partition available. If fdisk dialogs are disabled then returns -EINVAL.
735 *
736 * Returns: 0 on success, <0 on error, or 1 for non-free partno by Ask API.
737 */
738 int fdisk_partition_next_partno(
739 struct fdisk_partition *pa,
740 struct fdisk_context *cxt,
741 size_t *n)
742 {
743 if (!cxt || !n)
744 return -EINVAL;
745
746 if (pa && pa->partno_follow_default) {
747 size_t i;
748
749 DBG(PART, ul_debugobj(pa, "next partno (follow default)"));
750
751 for (i = 0; i < cxt->label->nparts_max; i++) {
752 if (!fdisk_is_partition_used(cxt, i)) {
753 *n = i;
754 return 0;
755 }
756 }
757 return -ERANGE;
758
759 }
760
761 if (pa && fdisk_partition_has_partno(pa)) {
762
763 DBG(PART, ul_debugobj(pa, "next partno (specified=%zu)", pa->partno));
764
765 if (pa->partno >= cxt->label->nparts_max ||
766 fdisk_is_partition_used(cxt, pa->partno))
767 return -ERANGE;
768 *n = pa->partno;
769 return 0;
770
771 }
772
773 if (fdisk_has_dialogs(cxt))
774 return fdisk_ask_partnum(cxt, n, 1);
775
776 return -EINVAL;
777 }
778
779 static int probe_partition_content(struct fdisk_context *cxt, struct fdisk_partition *pa)
780 {
781 int rc = 1; /* nothing */
782
783 DBG(PART, ul_debugobj(pa, "start probe #%zu partition [cxt %p] >>>", pa->partno, cxt));
784
785 /* zeroize the current setting */
786 strdup_to_struct_member(pa, fstype, NULL);
787 strdup_to_struct_member(pa, fsuuid, NULL);
788 strdup_to_struct_member(pa, fslabel, NULL);
789
790 if (!fdisk_partition_has_start(pa) ||
791 !fdisk_partition_has_size(pa))
792 goto done;
793
794 #ifdef HAVE_LIBBLKID
795 else {
796 uintmax_t start, size;
797
798 blkid_probe pr = blkid_new_probe();
799 if (!pr)
800 goto done;
801
802 DBG(PART, ul_debugobj(pa, "blkid prober: %p", pr));
803
804 blkid_probe_enable_superblocks(pr, 1);
805 blkid_probe_set_superblocks_flags(pr,
806 BLKID_SUBLKS_MAGIC |
807 BLKID_SUBLKS_TYPE |
808 BLKID_SUBLKS_LABEL |
809 BLKID_SUBLKS_UUID |
810 BLKID_SUBLKS_BADCSUM);
811
812 start = fdisk_partition_get_start(pa) * fdisk_get_sector_size(cxt);
813 size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cxt);
814
815 if (blkid_probe_set_device(pr, cxt->dev_fd, start, size) == 0
816 && blkid_do_fullprobe(pr) == 0) {
817
818 const char *data;
819 rc = 0;
820
821 if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
822 rc = strdup_to_struct_member(pa, fstype, data);
823
824 if (!rc && !blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
825 rc = strdup_to_struct_member(pa, fslabel, data);
826
827 if (!rc && !blkid_probe_lookup_value(pr, "UUID", &data, NULL))
828 rc = strdup_to_struct_member(pa, fsuuid, data);
829 }
830
831 blkid_free_probe(pr);
832 pa->fs_probed = 1;
833 }
834 #endif /* HAVE_LIBBLKID */
835
836 done:
837 DBG(PART, ul_debugobj(pa, "<<< end probe #%zu partition[cxt %p, rc=%d]", pa->partno, cxt, rc));
838 return rc;
839 }
840
841 /**
842 * fdisk_partition_to_string:
843 * @pa: partition
844 * @cxt: context
845 * @id: field (FDISK_FIELD_*)
846 * @data: returns string with allocated data
847 *
848 * Returns info about partition converted to printable string.
849 *
850 * For example
851 * <informalexample>
852 * <programlisting>
853 * struct fdisk_partition *pa;
854 *
855 * fdisk_get_partition(cxt, 0, &pa);
856 * fdisk_partition_to_string(pa, FDISK_FIELD_UUID, &data);
857 * printf("first partition uuid: %s\n", data);
858 * free(data);
859 * fdisk_unref_partition(pa);
860 * </programlisting>
861 * </informalexample>
862 *
863 * returns UUID for the first partition.
864 *
865 * Returns: 0 on success, otherwise, a corresponding error.
866 */
867 int fdisk_partition_to_string(struct fdisk_partition *pa,
868 struct fdisk_context *cxt,
869 int id,
870 char **data)
871 {
872 char *p = NULL;
873 int rc = 0;
874 uint64_t x;
875
876 if (!pa || !cxt || !data)
877 return -EINVAL;
878
879 switch (id) {
880 case FDISK_FIELD_DEVICE:
881 if (pa->freespace)
882 p = strdup(_("Free space"));
883 else if (fdisk_partition_has_partno(pa) && cxt->dev_path) {
884 if (cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
885 rc = asprintf(&p, "%c", (int) pa->partno + 'a');
886 else
887 p = fdisk_partname(cxt->dev_path, pa->partno + 1);
888 }
889 break;
890 case FDISK_FIELD_BOOT:
891 p = fdisk_partition_is_bootable(pa) ? strdup("*") : NULL;
892 break;
893 case FDISK_FIELD_START:
894 if (fdisk_partition_has_start(pa)) {
895 x = fdisk_cround(cxt, pa->start);
896 rc = pa->start_post ?
897 asprintf(&p, "%"PRIu64"%c", x, pa->start_post) :
898 asprintf(&p, "%"PRIu64, x);
899 }
900 break;
901 case FDISK_FIELD_END:
902 if (fdisk_partition_has_end(pa)) {
903 x = fdisk_cround(cxt, fdisk_partition_get_end(pa));
904 rc = pa->end_post ?
905 asprintf(&p, "%"PRIu64"%c", x, pa->end_post) :
906 asprintf(&p, "%"PRIu64, x);
907 }
908 break;
909 case FDISK_FIELD_SIZE:
910 if (fdisk_partition_has_size(pa)) {
911 uint64_t sz = pa->size * cxt->sector_size;
912
913 switch (cxt->sizeunit) {
914 case FDISK_SIZEUNIT_BYTES:
915 rc = asprintf(&p, "%"PRIu64"", sz);
916 break;
917 case FDISK_SIZEUNIT_HUMAN:
918 if (fdisk_is_details(cxt))
919 rc = pa->size_post ?
920 asprintf(&p, "%"PRIu64"%c", sz, pa->size_post) :
921 asprintf(&p, "%"PRIu64, sz);
922 else {
923 p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
924 if (!p)
925 rc = -ENOMEM;
926 }
927 break;
928 }
929 }
930 break;
931 case FDISK_FIELD_CYLINDERS:
932 {
933 uintmax_t sz = fdisk_partition_has_size(pa) ? pa->size : 0;
934 if (sz)
935 /* Why we need to cast that to uintmax_t? */
936 rc = asprintf(&p, "%ju", (uintmax_t)(sz / (cxt->geom.heads * cxt->geom.sectors)) + 1);
937 break;
938 }
939 case FDISK_FIELD_SECTORS:
940 rc = asprintf(&p, "%ju",
941 fdisk_partition_has_size(pa) ? (uintmax_t) pa->size : 0);
942 break;
943 case FDISK_FIELD_BSIZE:
944 rc = asprintf(&p, "%"PRIu64, pa->bsize);
945 break;
946 case FDISK_FIELD_FSIZE:
947 rc = asprintf(&p, "%"PRIu64, pa->fsize);
948 break;
949 case FDISK_FIELD_CPG:
950 rc = asprintf(&p, "%"PRIu64, pa->cpg);
951 break;
952 case FDISK_FIELD_TYPE:
953 p = pa->type && pa->type->name ? strdup(_(pa->type->name)) : NULL;
954 break;
955 case FDISK_FIELD_TYPEID:
956 if (pa->type && fdisk_parttype_get_string(pa->type))
957 rc = asprintf(&p, "%s", fdisk_parttype_get_string(pa->type));
958 else if (pa->type)
959 rc = asprintf(&p, "%x", fdisk_parttype_get_code(pa->type));
960 break;
961 case FDISK_FIELD_UUID:
962 p = pa->uuid && *pa->uuid? strdup(pa->uuid) : NULL;
963 break;
964 case FDISK_FIELD_NAME:
965 p = pa->name && *pa->name ? strdup(pa->name) : NULL;
966 break;
967 case FDISK_FIELD_ATTR:
968 p = pa->attrs && *pa->attrs ? strdup(pa->attrs) : NULL;
969 break;
970 case FDISK_FIELD_SADDR:
971 p = pa->start_chs && *pa->start_chs ? strdup(pa->start_chs) : NULL;
972 break;
973 case FDISK_FIELD_EADDR:
974 p = pa->end_chs && *pa->end_chs? strdup(pa->end_chs) : NULL;
975 break;
976 case FDISK_FIELD_FSUUID:
977 if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
978 p = pa->fsuuid && *pa->fsuuid ? strdup(pa->fsuuid) : NULL;
979 break;
980 case FDISK_FIELD_FSLABEL:
981 if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
982 p = pa->fslabel && *pa->fslabel ? strdup(pa->fslabel) : NULL;
983 break;
984 case FDISK_FIELD_FSTYPE:
985 if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
986 p = pa->fstype && *pa->fstype ? strdup(pa->fstype) : NULL;
987 break;
988 default:
989 return -EINVAL;
990 }
991
992 if (rc < 0) {
993 rc = -ENOMEM;
994 free(p);
995 p = NULL;
996
997 } else if (rc > 0)
998 rc = 0;
999
1000 *data = p;
1001
1002 return rc;
1003 }
1004
1005
1006 /**
1007 * fdisk_get_partition:
1008 * @cxt: context
1009 * @partno: partition number (0 is the first partition)
1010 * @pa: returns data about partition
1011 *
1012 * Reads disklabel and fills in @pa with data about partition @n.
1013 *
1014 * Note that partno may address unused partition and then this function does
1015 * not fill anything to @pa. See fdisk_is_partition_used(). If @pa points to
1016 * NULL then the function allocates a newly allocated fdisk_partition struct,
1017 * use fdisk_unref_partition() to deallocate.
1018 *
1019 * Returns: 0 on success, otherwise, a corresponding error.
1020 */
1021 int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
1022 struct fdisk_partition **pa)
1023 {
1024 int rc;
1025 struct fdisk_partition *np = NULL;
1026
1027 if (!cxt || !cxt->label || !pa)
1028 return -EINVAL;
1029 if (!cxt->label->op->get_part)
1030 return -ENOSYS;
1031 if (!fdisk_is_partition_used(cxt, partno))
1032 return -EINVAL;
1033
1034 if (!*pa) {
1035 np = *pa = fdisk_new_partition();
1036 if (!*pa)
1037 return -ENOMEM;
1038 } else
1039 fdisk_reset_partition(*pa);
1040
1041 (*pa)->partno = partno;
1042 rc = cxt->label->op->get_part(cxt, partno, *pa);
1043
1044 if (rc) {
1045 if (np) {
1046 fdisk_unref_partition(np);
1047 *pa = NULL;
1048 } else
1049 fdisk_reset_partition(*pa);
1050 } else
1051 (*pa)->size_explicit = 1;
1052 return rc;
1053 }
1054
1055 static struct fdisk_partition *area_by_offset(
1056 struct fdisk_table *tb,
1057 struct fdisk_partition *cur,
1058 fdisk_sector_t off)
1059 {
1060 struct fdisk_partition *pa = NULL;
1061 struct fdisk_iter itr;
1062
1063 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
1064
1065 while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
1066 if (!fdisk_partition_has_start(pa) || !fdisk_partition_has_size(pa))
1067 continue;
1068 if (fdisk_partition_is_nested(cur) &&
1069 pa->parent_partno != cur->parent_partno)
1070 continue;
1071 if (off >= pa->start && off < pa->start + pa->size)
1072 return pa;
1073 }
1074
1075 return NULL;
1076 }
1077
1078 static int resize_get_first_possible(
1079 struct fdisk_table *tb,
1080 struct fdisk_partition *cur,
1081 fdisk_sector_t *start)
1082 {
1083 struct fdisk_partition *pa = NULL, *first = NULL;
1084 struct fdisk_iter itr;
1085
1086 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
1087
1088 *start = 0;
1089 DBG(TAB, ul_debugobj(tb, "checking first possible before start=%ju", (uintmax_t) cur->start));
1090
1091
1092 while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
1093
1094 if (pa->start > cur->start || pa == cur)
1095 break;
1096
1097 DBG(TAB, ul_debugobj(tb, " checking entry %p [partno=%zu start=%ju, end=%ju, size=%ju%s%s%s]",
1098 pa,
1099 fdisk_partition_get_partno(pa),
1100 (uintmax_t) fdisk_partition_get_start(pa),
1101 (uintmax_t) fdisk_partition_get_end(pa),
1102 (uintmax_t) fdisk_partition_get_size(pa),
1103 fdisk_partition_is_freespace(pa) ? " freespace" : "",
1104 fdisk_partition_is_nested(pa) ? " nested" : "",
1105 fdisk_partition_is_container(pa) ? " container" : ""));
1106
1107
1108 if (!fdisk_partition_is_freespace(pa)) {
1109 DBG(TAB, ul_debugobj(tb, " ignored (no freespace)"));
1110 first = NULL;
1111 continue;
1112 }
1113 if (!fdisk_partition_has_start(pa) || !fdisk_partition_has_size(pa)) {
1114 DBG(TAB, ul_debugobj(tb, " ignored (no start/size)"));
1115 first = NULL;
1116 continue;
1117 }
1118 /* The current is nested, free space has to be nested within the same parent */
1119 if (fdisk_partition_is_nested(cur)
1120 && pa->parent_partno != cur->parent_partno) {
1121 DBG(TAB, ul_debugobj(tb, " ignore (nested required)"));
1122 first = NULL;
1123 continue;
1124 }
1125 if (pa->start + pa->size <= cur->start) {
1126 first = pa;
1127 DBG(TAB, ul_debugobj(tb, " entry usable"));
1128 }
1129 }
1130
1131 if (first)
1132 *start = first->start;
1133 else
1134 DBG(PART, ul_debugobj(cur, "resize: nothing usable before %ju", (uintmax_t) cur->start));
1135
1136 return first ? 0 : -1;
1137 }
1138
1139 /*
1140 * Verify that area addressed by @start is freespace or the @cur[rent]
1141 * partition and continue to the next table entries until it's freespace, and
1142 * counts size of all this space.
1143 *
1144 * This is core of the partition start offset move operation. We can move the
1145 * start within the current partition to another free space. It's
1146 * forbidden to move start of the partition to another already defined
1147 * partition.
1148 */
1149 static int resize_get_last_possible(
1150 struct fdisk_table *tb,
1151 struct fdisk_partition *cur,
1152 fdisk_sector_t start,
1153 fdisk_sector_t *maxsz)
1154 {
1155 struct fdisk_partition *pa = NULL, *last = NULL;
1156 struct fdisk_iter itr;
1157
1158 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
1159
1160 *maxsz = 0;
1161 DBG(TAB, ul_debugobj(tb, "checking last possible for start=%ju", (uintmax_t) start));
1162
1163
1164 while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
1165
1166 DBG(TAB, ul_debugobj(tb, " checking entry %p [partno=%zu start=%ju, end=%ju, size=%ju%s%s%s]",
1167 pa,
1168 fdisk_partition_get_partno(pa),
1169 (uintmax_t) fdisk_partition_get_start(pa),
1170 (uintmax_t) fdisk_partition_get_end(pa),
1171 (uintmax_t) fdisk_partition_get_size(pa),
1172 fdisk_partition_is_freespace(pa) ? " freespace" : "",
1173 fdisk_partition_is_nested(pa) ? " nested" : "",
1174 fdisk_partition_is_container(pa) ? " container" : ""));
1175
1176 if (!fdisk_partition_has_start(pa) ||
1177 !fdisk_partition_has_size(pa) ||
1178 (fdisk_partition_is_container(pa) && pa != cur)) {
1179 DBG(TAB, ul_debugobj(tb, " ignored (no start/size or container)"));
1180 continue;
1181 }
1182
1183 if (fdisk_partition_is_nested(pa)
1184 && fdisk_partition_is_container(cur)
1185 && pa->parent_partno == cur->partno) {
1186 DBG(TAB, ul_debugobj(tb, " ignore (nested child of the current partition)"));
1187 continue;
1188 }
1189
1190 /* The current is nested, free space has to be nested within the same parent */
1191 if (fdisk_partition_is_nested(cur)
1192 && pa->parent_partno != cur->parent_partno) {
1193 DBG(TAB, ul_debugobj(tb, " ignore (nested required)"));
1194 continue;
1195 }
1196
1197 if (!last) {
1198 if (start >= pa->start && start < pa->start + pa->size) {
1199 if (fdisk_partition_is_freespace(pa) || pa == cur) {
1200 DBG(TAB, ul_debugobj(tb, " accepted as last"));
1201 last = pa;
1202 } else {
1203 DBG(TAB, ul_debugobj(tb, " failed to set last"));
1204 break;
1205 }
1206
1207
1208 *maxsz = pa->size - (start - pa->start);
1209 DBG(TAB, ul_debugobj(tb, " new max=%ju", (uintmax_t) *maxsz));
1210 }
1211 } else if (!fdisk_partition_is_freespace(pa) && pa != cur) {
1212 DBG(TAB, ul_debugobj(tb, " no free space behind current"));
1213 break;
1214 } else {
1215 last = pa;
1216 *maxsz = pa->size - (start - pa->start);
1217 DBG(TAB, ul_debugobj(tb, " new max=%ju (last updated)", (uintmax_t) *maxsz));
1218 }
1219 }
1220
1221 if (last)
1222 DBG(PART, ul_debugobj(cur, "resize: max size=%ju", (uintmax_t) *maxsz));
1223 else
1224 DBG(PART, ul_debugobj(cur, "resize: nothing usable after %ju", (uintmax_t) start));
1225
1226 return last ? 0 : -1;
1227 }
1228
1229 /*
1230 * Uses template @tpl to recount start and size change of the partition @res. The
1231 * @tpl->size and @tpl->start are interpreted as relative to the current setting.
1232 */
1233 static int recount_resize(
1234 struct fdisk_context *cxt, size_t partno,
1235 struct fdisk_partition *res, struct fdisk_partition *tpl)
1236 {
1237 fdisk_sector_t start, size, xsize;
1238 struct fdisk_partition *cur = NULL;
1239 struct fdisk_table *tb = NULL;
1240 int rc;
1241
1242 DBG(PART, ul_debugobj(tpl, ">>> resize requested"));
1243
1244 FDISK_INIT_UNDEF(start);
1245 FDISK_INIT_UNDEF(size);
1246
1247 rc = fdisk_get_partitions(cxt, &tb);
1248 if (!rc) {
1249 /* For resize we do not follow grain to detect free-space, but
1250 * we support to resize with very small granulation. */
1251 unsigned long org = cxt->grain;
1252
1253 cxt->grain = cxt->sector_size;
1254 rc = fdisk_get_freespaces(cxt, &tb);
1255 cxt->grain = org;
1256 }
1257 if (rc) {
1258 fdisk_unref_table(tb);
1259 return rc;
1260 }
1261
1262 fdisk_table_sort_partitions(tb, fdisk_partition_cmp_start);
1263
1264 DBG(PART, ul_debugobj(tpl, "resize partition partno=%zu in table:", partno));
1265 ON_DBG(PART, fdisk_debug_print_table(tb));
1266
1267 cur = fdisk_table_get_partition_by_partno(tb, partno);
1268 if (!cur) {
1269 fdisk_unref_table(tb);
1270 return -EINVAL;
1271 }
1272
1273 /* 1a) set new start - change relative to the current on-disk setting */
1274 if (tpl->movestart && fdisk_partition_has_start(tpl)) {
1275 start = fdisk_partition_get_start(cur);
1276 if (tpl->movestart == FDISK_MOVE_DOWN) {
1277 if (fdisk_partition_get_start(tpl) > start)
1278 goto erange;
1279 start -= fdisk_partition_get_start(tpl);
1280 } else
1281 start += fdisk_partition_get_start(tpl);
1282
1283 DBG(PART, ul_debugobj(tpl, "resize: moving start %s relative, new start: %ju",
1284 tpl->movestart == FDISK_MOVE_DOWN ? "DOWN" : "UP", (uintmax_t)start));
1285
1286 /* 1b) set new start - try freespace before the current partition */
1287 } else if (tpl->movestart == FDISK_MOVE_DOWN) {
1288
1289 if (resize_get_first_possible(tb, cur, &start) != 0)
1290 goto erange;
1291
1292 DBG(PART, ul_debugobj(tpl, "resize: moving start DOWN (first possible), new start: %ju",
1293 (uintmax_t)start));
1294
1295 /* 1c) set new start - absolute number */
1296 } else if (fdisk_partition_has_start(tpl)) {
1297 start = fdisk_partition_get_start(tpl);
1298 DBG(PART, ul_debugobj(tpl, "resize: moving start to absolute offset: %ju",
1299 (uintmax_t)start));
1300 }
1301
1302 /* 2) verify that start is within the current partition or any freespace area */
1303 if (!FDISK_IS_UNDEF(start)) {
1304 struct fdisk_partition *area = area_by_offset(tb, cur, start);
1305
1306 if (area == cur)
1307 DBG(PART, ul_debugobj(tpl, "resize: start points to the current partition"));
1308 else if (area && fdisk_partition_is_freespace(area))
1309 DBG(PART, ul_debugobj(tpl, "resize: start points to freespace"));
1310 else if (!area && start >= cxt->first_lba && start < cxt->first_lba + (cxt->grain / cxt->sector_size))
1311 DBG(PART, ul_debugobj(tpl, "resize: start points before first partition"));
1312 else {
1313 DBG(PART, ul_debugobj(tpl, "resize: start verification failed"));
1314 goto erange;
1315 }
1316 } else {
1317 /* no change, start points to the current partition */
1318 DBG(PART, ul_debugobj(tpl, "resize: start unchanged"));
1319 start = fdisk_partition_get_start(cur);
1320 }
1321
1322 /* 3a) set new size -- reduce */
1323 if (tpl->resize == FDISK_RESIZE_REDUCE && fdisk_partition_has_size(tpl)) {
1324 DBG(PART, ul_debugobj(tpl, "resize: reduce"));
1325 size = fdisk_partition_get_size(cur);
1326 if (fdisk_partition_get_size(tpl) > size)
1327 goto erange;
1328 size -= fdisk_partition_get_size(tpl);
1329
1330 /* 3b) set new size -- enlarge */
1331 } else if (tpl->resize == FDISK_RESIZE_ENLARGE && fdisk_partition_has_size(tpl)) {
1332 DBG(PART, ul_debugobj(tpl, "resize: enlarge"));
1333 size = fdisk_partition_get_size(cur);
1334 size += fdisk_partition_get_size(tpl);
1335
1336 /* 3c) set new size -- no size specified, enlarge to all freespace */
1337 } else if (tpl->resize == FDISK_RESIZE_ENLARGE) {
1338 DBG(PART, ul_debugobj(tpl, "resize: enlarge to all possible"));
1339 if (resize_get_last_possible(tb, cur, start, &size))
1340 goto erange;
1341
1342 /* 3d) set new size -- absolute number */
1343 } else if (fdisk_partition_has_size(tpl)) {
1344 DBG(PART, ul_debugobj(tpl, "resize: new absolute size"));
1345 size = fdisk_partition_get_size(tpl);
1346 }
1347
1348 /* 4) verify that size is within the current partition or next free space */
1349 xsize = !FDISK_IS_UNDEF(size) ? size : fdisk_partition_get_size(cur);
1350
1351 if (fdisk_partition_has_size(cur)) {
1352 fdisk_sector_t maxsz;
1353
1354 if (resize_get_last_possible(tb, cur, start, &maxsz))
1355 goto erange;
1356 DBG(PART, ul_debugobj(tpl, "resize: size=%ju, max=%ju",
1357 (uintmax_t) xsize, (uintmax_t) maxsz));
1358 if (xsize > maxsz)
1359 goto erange;
1360 }
1361
1362 if (FDISK_IS_UNDEF(size)) {
1363 DBG(PART, ul_debugobj(tpl, "resize: size unchanged (undefined)"));
1364 }
1365
1366
1367 DBG(PART, ul_debugobj(tpl, "<<< resize: SUCCESS: start %ju->%ju; size %ju->%ju",
1368 (uintmax_t) fdisk_partition_get_start(cur), (uintmax_t) start,
1369 (uintmax_t) fdisk_partition_get_size(cur), (uintmax_t) size));
1370 res->start = start;
1371 res->size = size;
1372 fdisk_unref_table(tb);
1373 return 0;
1374 erange:
1375 DBG(PART, ul_debugobj(tpl, "<<< resize: FAILED"));
1376 fdisk_warnx(cxt, _("Failed to resize partition #%zu."), partno + 1);
1377 fdisk_unref_table(tb);
1378 return -ERANGE;
1379
1380 }
1381
1382 /**
1383 * fdisk_set_partition:
1384 * @cxt: context
1385 * @partno: partition number (0 is the first partition)
1386 * @pa: new partition setting
1387 *
1388 * Modifies disklabel according to setting with in @pa.
1389 *
1390 * Returns: 0 on success, <0 on error.
1391 */
1392 int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
1393 struct fdisk_partition *pa)
1394 {
1395 struct fdisk_partition *xpa = pa, *tmp = NULL;
1396 int rc, wipe = 0;
1397
1398 if (!cxt || !cxt->label || !pa)
1399 return -EINVAL;
1400 if (!cxt->label->op->set_part)
1401 return -ENOSYS;
1402
1403 pa->fs_probed = 0;
1404
1405 if (!fdisk_is_partition_used(cxt, partno)) {
1406 pa->partno = partno;
1407 return fdisk_add_partition(cxt, pa, NULL);
1408 }
1409
1410 if (pa->resize || pa->movestart
1411 || fdisk_partition_has_start(pa) || fdisk_partition_has_size(pa)) {
1412 xpa = __copy_partition(pa);
1413 if (!xpa) {
1414 rc = -ENOMEM;
1415 goto done;
1416 }
1417 xpa->movestart = 0;
1418 xpa->resize = 0;
1419 FDISK_INIT_UNDEF(xpa->size);
1420 FDISK_INIT_UNDEF(xpa->start);
1421
1422 rc = recount_resize(cxt, partno, xpa, pa);
1423 if (rc)
1424 goto done;
1425 }
1426
1427 DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju)",
1428 partno, xpa,
1429 (uintmax_t) fdisk_partition_get_start(xpa),
1430 (uintmax_t) fdisk_partition_get_end(xpa),
1431 (uintmax_t) fdisk_partition_get_size(xpa)));
1432
1433 /* disable wipe for old offset/size setting */
1434 if (fdisk_get_partition(cxt, partno, &tmp) == 0 && tmp) {
1435 wipe = fdisk_set_wipe_area(cxt, fdisk_partition_get_start(tmp),
1436 fdisk_partition_get_size(tmp), FALSE);
1437 fdisk_unref_partition(tmp);
1438 }
1439
1440 /* call label driver */
1441 rc = cxt->label->op->set_part(cxt, partno, xpa);
1442
1443 /* enable wipe for new offset/size */
1444 if (!rc && wipe)
1445 fdisk_wipe_partition(cxt, partno, TRUE);
1446 done:
1447 DBG(CXT, ul_debugobj(cxt, "set_partition() rc=%d", rc));
1448 if (xpa != pa)
1449 fdisk_unref_partition(xpa);
1450 return rc;
1451 }
1452
1453 /**
1454 * fdisk_wipe_partition:
1455 * @cxt: fdisk context
1456 * @partno: partition number
1457 * @enable: 0 or 1
1458 *
1459 * Enable/disable filesystems/RAIDs wiping in area defined by partition start and size.
1460 *
1461 * Returns: <0 in case of error, 0 on success
1462 * Since: 2.29
1463 */
1464 int fdisk_wipe_partition(struct fdisk_context *cxt, size_t partno, int enable)
1465 {
1466 struct fdisk_partition *pa = NULL;
1467 int rc;
1468
1469 rc = fdisk_get_partition(cxt, partno, &pa);
1470 if (rc)
1471 return rc;
1472
1473 rc = fdisk_set_wipe_area(cxt, fdisk_partition_get_start(pa),
1474 fdisk_partition_get_size(pa), enable);
1475 fdisk_unref_partition(pa);
1476 return rc < 0 ? rc : 0;
1477 }
1478
1479 /**
1480 * fdisk_partition_has_wipe:
1481 * @cxt: fdisk context
1482 * @pa: partition
1483 *
1484 * Since: 2.30
1485 *
1486 * Returns: 1 if the area specified by @pa will be wiped by write command, or 0.
1487 */
1488 int fdisk_partition_has_wipe(struct fdisk_context *cxt, struct fdisk_partition *pa)
1489 {
1490 return fdisk_has_wipe_area(cxt, fdisk_partition_get_start(pa),
1491 fdisk_partition_get_size(pa));
1492 }
1493
1494
1495 /**
1496 * fdisk_add_partition:
1497 * @cxt: fdisk context
1498 * @pa: template for the partition (or NULL)
1499 * @partno: NULL or returns new partition number
1500 *
1501 * If @pa is not specified or any @pa item is missing the libfdisk will ask by
1502 * fdisk_ask_ API.
1503 *
1504 * The @pa template is important for non-interactive partitioning,
1505 * especially for MBR where is necessary to differentiate between
1506 * primary/logical; this is done by start offset or/and partno.
1507 * The rules for MBR:
1508 *
1509 * A) template specifies start within extended partition: add logical
1510 * B) template specifies start out of extended partition: add primary
1511 * C) template specifies start (or default), partno < 4: add primary
1512 * D) template specifies default start, partno >= 4: add logical
1513 *
1514 * otherwise MBR driver uses Ask-API to get missing information.
1515 *
1516 * Adds a new partition to disklabel.
1517 *
1518 * Returns: 0 on success, <0 on error.
1519 */
1520 int fdisk_add_partition(struct fdisk_context *cxt,
1521 struct fdisk_partition *pa,
1522 size_t *partno)
1523 {
1524 int rc;
1525
1526 if (!cxt || !cxt->label)
1527 return -EINVAL;
1528 if (!cxt->label->op->add_part)
1529 return -ENOSYS;
1530 if (fdisk_missing_geometry(cxt))
1531 return -EINVAL;
1532
1533 if (pa) {
1534 pa->fs_probed = 0;
1535 DBG(CXT, ul_debugobj(cxt, "adding new partition %p", pa));
1536 if (fdisk_partition_has_start(pa))
1537 DBG(CXT, ul_debugobj(cxt, " start: %ju", (uintmax_t) fdisk_partition_get_start(pa)));
1538 if (fdisk_partition_has_end(pa))
1539 DBG(CXT, ul_debugobj(cxt, " end: %ju", (uintmax_t) fdisk_partition_get_end(pa)));
1540 if (fdisk_partition_has_size(pa))
1541 DBG(CXT, ul_debugobj(cxt, " size: %ju", (uintmax_t) fdisk_partition_get_size(pa)));
1542
1543 DBG(CXT, ul_debugobj(cxt, " defaults: start=%s, end=%s, partno=%s",
1544 pa->start_follow_default ? "yes" : "no",
1545 pa->end_follow_default ? "yes" : "no",
1546 pa->partno_follow_default ? "yes" : "no"));
1547 } else
1548 DBG(CXT, ul_debugobj(cxt, "adding partition"));
1549
1550 rc = cxt->label->op->add_part(cxt, pa, partno);
1551
1552 DBG(CXT, ul_debugobj(cxt, "add partition done (rc=%d)", rc));
1553 return rc;
1554 }
1555
1556 /**
1557 * fdisk_delete_partition:
1558 * @cxt: fdisk context
1559 * @partno: partition number to delete (0 is the first partition)
1560 *
1561 * Deletes a @partno partition from disklabel.
1562 *
1563 * Returns: 0 on success, <0 on error
1564 */
1565 int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
1566 {
1567 if (!cxt || !cxt->label)
1568 return -EINVAL;
1569 if (!cxt->label->op->del_part)
1570 return -ENOSYS;
1571
1572 fdisk_wipe_partition(cxt, partno, 0);
1573
1574 DBG(CXT, ul_debugobj(cxt, "deleting %s partition number %zd",
1575 cxt->label->name, partno));
1576 return cxt->label->op->del_part(cxt, partno);
1577 }
1578
1579 /**
1580 * fdisk_delete_all_partitions:
1581 * @cxt: fdisk context
1582 *
1583 * Delete all used partitions from disklabel.
1584 *
1585 * Returns: 0 on success, otherwise, a corresponding error.
1586 */
1587 int fdisk_delete_all_partitions(struct fdisk_context *cxt)
1588 {
1589 size_t i;
1590 int rc = 0;
1591
1592 if (!cxt || !cxt->label)
1593 return -EINVAL;
1594
1595 for (i = 0; i < cxt->label->nparts_max; i++) {
1596
1597 if (!fdisk_is_partition_used(cxt, i))
1598 continue;
1599 rc = fdisk_delete_partition(cxt, i);
1600 if (rc)
1601 break;
1602 }
1603
1604 return rc;
1605 }
1606
1607 /**
1608 * fdisk_is_partition_used:
1609 * @cxt: context
1610 * @n: partition number (0 is the first partition)
1611 *
1612 * Check if the partition number @n is used by partition table. This function
1613 * does not check if the device is used (e.g. mounted) by system!
1614 *
1615 * This is faster than fdisk_get_partition() + fdisk_partition_is_used().
1616 *
1617 * Returns: 0 or 1
1618 */
1619 int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n)
1620 {
1621 if (!cxt || !cxt->label)
1622 return -EINVAL;
1623 if (!cxt->label->op->part_is_used)
1624 return -ENOSYS;
1625
1626 return cxt->label->op->part_is_used(cxt, n);
1627 }
1628
1629
1630 /**
1631 * fdisk_partition_max_size:
1632 * @cxt: context
1633 * @n: partition number (0 is the first partition)
1634 * @maxsz: returns maximum size of partition
1635 *
1636 * Find maximum size the partition can be resized to.
1637 * Takes into account free space between this partition and the next one.
1638 *
1639 * Returns: 0 on success, <0 on error.
1640 */
1641 int fdisk_partition_get_max_size(struct fdisk_context *cxt, size_t n,
1642 fdisk_sector_t *maxsz)
1643 {
1644 struct fdisk_partition *cur = NULL;
1645 struct fdisk_table *tb = NULL;
1646 int rc;
1647
1648 rc = fdisk_get_partitions(cxt, &tb);
1649 if (rc)
1650 goto out;
1651
1652 rc = fdisk_get_freespaces(cxt, &tb);
1653 if (rc)
1654 goto out;
1655
1656 rc = fdisk_table_sort_partitions(tb, fdisk_partition_cmp_start);
1657 if (rc)
1658 goto out;
1659
1660 cur = fdisk_table_get_partition_by_partno(tb, n);
1661 if (!cur)
1662 goto einval;
1663
1664 if (!fdisk_partition_has_start(cur))
1665 goto einval;
1666
1667 if (resize_get_last_possible(tb, cur,
1668 fdisk_partition_get_start(cur), maxsz))
1669 goto einval;
1670
1671 out:
1672 fdisk_unref_partition(cur);
1673 fdisk_unref_table(tb);
1674
1675 return rc;
1676
1677 einval:
1678 rc = -EINVAL;
1679 goto out;
1680 }