7 * @short_description: container for fdisk partitions
9 * The fdisk_table is simple container for fdisk_partitions. The table is no
10 * directly connected to label data (partition table), and table changes don't
11 * affect in-memory or on-disk data.
17 * The table is a container for struct fdisk_partition entries. The container
18 * does not have any real connection with label (partition table) and with
21 * Returns: newly allocated table struct.
23 struct fdisk_table
*fdisk_new_table(void)
25 struct fdisk_table
*tb
= NULL
;
27 tb
= calloc(1, sizeof(*tb
));
31 DBG(TAB
, ul_debugobj(tb
, "alloc"));
33 INIT_LIST_HEAD(&tb
->parts
);
41 * Removes all entries (partitions) from the table. The partitions with zero
42 * reference count will be deallocated. This function does not modify partition
45 * Returns: 0 on success or negative number in case of error.
47 int fdisk_reset_table(struct fdisk_table
*tb
)
52 DBG(TAB
, ul_debugobj(tb
, "reset"));
54 while (!list_empty(&tb
->parts
)) {
55 struct fdisk_partition
*pa
= list_entry(tb
->parts
.next
,
56 struct fdisk_partition
, parts
);
57 fdisk_table_remove_partition(tb
, pa
);
68 * Increments reference counter.
70 void fdisk_ref_table(struct fdisk_table
*tb
)
80 * Decrements reference counter, on zero the @tb is automatically
83 void fdisk_unref_table(struct fdisk_table
*tb
)
89 if (tb
->refcount
<= 0) {
90 fdisk_reset_table(tb
);
92 DBG(TAB
, ul_debugobj(tb
, "free"));
98 * fdisk_table_is_empty:
101 * Returns: 1 if the table is without filesystems, or 0.
103 int fdisk_table_is_empty(struct fdisk_table
*tb
)
105 return tb
== NULL
|| list_empty(&tb
->parts
) ? 1 : 0;
109 * fdisk_table_get_nents:
110 * @tb: pointer to tab
112 * Returns: number of entries in table.
114 size_t fdisk_table_get_nents(struct fdisk_table
*tb
)
116 return tb
? tb
->nents
: 0;
120 * fdisk_table_next_partition:
123 * @pa: returns the next tab entry
125 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
130 * while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
136 int fdisk_table_next_partition(
137 struct fdisk_table
*tb
,
138 struct fdisk_iter
*itr
,
139 struct fdisk_partition
**pa
)
143 if (!tb
|| !itr
|| !pa
)
148 FDISK_ITER_INIT(itr
, &tb
->parts
);
149 if (itr
->p
!= itr
->head
) {
150 FDISK_ITER_ITERATE(itr
, *pa
, struct fdisk_partition
, parts
);
158 * fdisk_table_get_partition:
160 * @n: number of entry in table
162 * Returns: n-th entry from table or NULL
164 struct fdisk_partition
*fdisk_table_get_partition(
165 struct fdisk_table
*tb
,
168 struct fdisk_partition
*pa
= NULL
;
169 struct fdisk_iter itr
;
174 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
176 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
186 * fdisk_table_get_partition_by_partno:
188 * @partno: partition number
190 * Returns: partition with @partno or NULL.
192 struct fdisk_partition
*fdisk_table_get_partition_by_partno(
193 struct fdisk_table
*tb
,
196 struct fdisk_partition
*pa
= NULL
;
197 struct fdisk_iter itr
;
202 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
204 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
205 if (pa
->partno
== partno
)
213 * fdisk_table_add_partition
217 * Adds a new entry to table and increment @pa reference counter. Don't forget to
218 * use fdisk_unref_partition() after fdisk_table_add_partition() if you want to keep
219 * the @pa referenced by the table only.
221 * Returns: 0 on success or negative number in case of error.
223 int fdisk_table_add_partition(struct fdisk_table
*tb
, struct fdisk_partition
*pa
)
228 if (!list_empty(&pa
->parts
))
231 fdisk_ref_partition(pa
);
232 list_add_tail(&pa
->parts
, &tb
->parts
);
235 DBG(TAB
, ul_debugobj(tb
, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
237 (uintmax_t) fdisk_partition_get_start(pa
),
238 fdisk_partition_has_end(pa
) ? (uintmax_t) fdisk_partition_get_end(pa
) : 0,
239 fdisk_partition_has_size(pa
) ? (uintmax_t) fdisk_partition_get_size(pa
) : 0,
240 fdisk_partition_is_freespace(pa
) ? "freespace" : "",
241 fdisk_partition_is_nested(pa
) ? "nested" : "",
242 fdisk_partition_is_container(pa
) ? "container" : "primary"));
246 /* inserts @pa after @poz */
247 static int table_insert_partition(
248 struct fdisk_table
*tb
,
249 struct fdisk_partition
*poz
,
250 struct fdisk_partition
*pa
)
255 fdisk_ref_partition(pa
);
257 list_add(&pa
->parts
, &poz
->parts
);
259 list_add(&pa
->parts
, &tb
->parts
);
262 DBG(TAB
, ul_debugobj(tb
, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
263 pa
, poz
? poz
: NULL
,
264 (uintmax_t) fdisk_partition_get_start(pa
),
265 (uintmax_t) fdisk_partition_get_end(pa
),
266 (uintmax_t) fdisk_partition_get_size(pa
),
267 fdisk_partition_is_freespace(pa
) ? "freespace" : "",
268 fdisk_partition_is_nested(pa
) ? "nested" : "",
269 fdisk_partition_is_container(pa
) ? "container" : ""));
274 * fdisk_table_remove_partition
278 * Removes the @pa from the table and de-increment reference counter of the @pa. The
279 * partition with zero reference counter will be deallocated. Don't forget to use
280 * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
283 * Returns: 0 on success or negative number in case of error.
285 int fdisk_table_remove_partition(struct fdisk_table
*tb
, struct fdisk_partition
*pa
)
290 DBG(TAB
, ul_debugobj(tb
, "remove entry %p", pa
));
291 list_del(&pa
->parts
);
292 INIT_LIST_HEAD(&pa
->parts
);
294 fdisk_unref_partition(pa
);
301 * fdisk_get_partitions
302 * @cxt: fdisk context
305 * This function adds partitions from disklabel to @table, it allocates a new
306 * table if @table points to NULL.
308 * Returns: 0 on success, otherwise, a corresponding error.
310 int fdisk_get_partitions(struct fdisk_context
*cxt
, struct fdisk_table
**tb
)
314 if (!cxt
|| !cxt
->label
|| !tb
)
316 if (!cxt
->label
->op
->get_part
)
319 DBG(CXT
, ul_debugobj(cxt
, " -- get table --"));
321 if (!*tb
&& !(*tb
= fdisk_new_table()))
324 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
325 struct fdisk_partition
*pa
= NULL
;
327 if (fdisk_get_partition(cxt
, i
, &pa
) != 0)
329 if (fdisk_partition_is_used(pa
))
330 fdisk_table_add_partition(*tb
, pa
);
331 fdisk_unref_partition(pa
);
337 void fdisk_debug_print_table(struct fdisk_table
*tb
)
339 struct fdisk_iter itr
;
340 struct fdisk_partition
*pa
;
342 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
343 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0)
344 ul_debugobj(tb
, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju%s%s%s] ",
346 (uintmax_t) fdisk_partition_get_start(pa
),
347 (uintmax_t) fdisk_partition_get_end(pa
),
348 (uintmax_t) fdisk_partition_get_size(pa
),
349 fdisk_partition_is_nested(pa
) ? " nested" : "",
350 fdisk_partition_is_freespace(pa
) ? " freespace" : "",
351 fdisk_partition_is_container(pa
) ? " container" : "");
356 typedef int (*fdisk_partcmp_t
)(struct fdisk_partition
*, struct fdisk_partition
*);
358 static int cmp_parts_wrapper(struct list_head
*a
, struct list_head
*b
, void *data
)
360 struct fdisk_partition
*pa
= list_entry(a
, struct fdisk_partition
, parts
),
361 *pb
= list_entry(b
, struct fdisk_partition
, parts
);
363 fdisk_partcmp_t cmp
= (fdisk_partcmp_t
) data
;
370 * fdisk_table_sort_partitions:
372 * @cmp: compare function
374 * Sort partition in the table.
376 * Returns: 0 on success, <0 on error.
378 int fdisk_table_sort_partitions(struct fdisk_table
*tb
,
379 int (*cmp
)(struct fdisk_partition
*,
380 struct fdisk_partition
*))
386 DBG(TAB, ul_debugobj(tb, "Before sort:"));
387 ON_DBG(TAB, fdisk_debug_print_table(tb));
390 list_sort(&tb
->parts
, cmp_parts_wrapper
, (void *) cmp
);
393 DBG(TAB, ul_debugobj(tb, "After sort:"));
394 ON_DBG(TAB, fdisk_debug_print_table(tb));
400 /* allocates a new freespace description */
401 static int new_freespace(struct fdisk_context
*cxt
,
402 fdisk_sector_t start
,
404 struct fdisk_partition
*parent
,
405 struct fdisk_partition
**pa
)
407 fdisk_sector_t aligned_start
, size
;
417 assert(start
>= cxt
->first_lba
);
421 aligned_start
= fdisk_align_lba_in_range(cxt
, start
, start
, end
);
422 size
= end
- aligned_start
+ 1ULL;
425 DBG(TAB
, ul_debug("ignore freespace (aligned size is zero)"));
429 *pa
= fdisk_new_partition();
433 (*pa
)->freespace
= 1;
434 (*pa
)->start
= aligned_start
;
438 (*pa
)->parent_partno
= parent
->partno
;
442 /* add freespace description to the right place within @tb */
443 static int table_add_freespace(
444 struct fdisk_context
*cxt
,
445 struct fdisk_table
*tb
,
446 fdisk_sector_t start
,
448 struct fdisk_partition
*parent
)
450 struct fdisk_partition
*pa
, *x
, *real_parent
= NULL
, *best
= NULL
;
451 struct fdisk_iter itr
;
456 rc
= new_freespace(cxt
, start
, end
, parent
, &pa
);
462 assert(fdisk_partition_has_start(pa
));
463 assert(fdisk_partition_has_end(pa
));
465 DBG(TAB
, ul_debugobj(tb
, "adding freespace"));
467 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
468 if (parent
&& fdisk_partition_has_partno(parent
)) {
469 while (fdisk_table_next_partition(tb
, &itr
, &x
) == 0) {
470 if (!fdisk_partition_has_partno(x
))
472 if (x
->partno
== parent
->partno
) {
478 DBG(TAB
, ul_debugobj(tb
, "not found freespace parent (partno=%zu)",
480 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
484 while (fdisk_table_next_partition(tb
, &itr
, &x
) == 0) {
485 fdisk_sector_t the_end
, best_end
= 0;
487 if (!fdisk_partition_has_end(x
))
490 the_end
= fdisk_partition_get_end(x
);
492 best_end
= fdisk_partition_get_end(best
);
494 if (the_end
< pa
->start
&& (!best
|| best_end
< the_end
))
498 if (!best
&& real_parent
)
500 rc
= table_insert_partition(tb
, best
, pa
);
502 fdisk_unref_partition(pa
);
504 DBG(TAB
, ul_debugobj(tb
, "adding freespace DONE [rc=%d]", rc
));
508 /* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
509 * that @parts has to be sorted by partition starts */
510 static int check_container_freespace(struct fdisk_context
*cxt
,
511 struct fdisk_table
*parts
,
512 struct fdisk_table
*tb
,
513 struct fdisk_partition
*cont
)
515 struct fdisk_iter itr
;
516 struct fdisk_partition
*pa
;
517 fdisk_sector_t x
, last
, grain
, lastplusoff
;
524 assert(fdisk_partition_has_start(cont
));
526 DBG(TAB
, ul_debugobj(tb
, "analyze container 0x%p", cont
));
528 last
= fdisk_partition_get_start(cont
);
529 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
530 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
532 DBG(CXT
, ul_debugobj(cxt
, "initialized: last=%ju, grain=%ju",
533 (uintmax_t)last
, (uintmax_t)grain
));
535 while (fdisk_table_next_partition(parts
, &itr
, &pa
) == 0) {
537 DBG(CXT
, ul_debugobj(cxt
, "partno=%zu, start=%ju",
538 pa
->partno
, (uintmax_t)pa
->start
));
540 if (!pa
->used
|| !fdisk_partition_is_nested(pa
)
541 || !fdisk_partition_has_start(pa
))
544 DBG(CXT
, ul_debugobj(cxt
, "freespace container analyze: partno=%zu, start=%ju, end=%ju",
546 (uintmax_t) fdisk_partition_get_start(pa
),
547 (uintmax_t) fdisk_partition_get_end(pa
)));
549 lastplusoff
= last
+ cxt
->first_lba
;
550 if (pa
->start
> lastplusoff
&& pa
->start
- lastplusoff
> grain
)
551 rc
= table_add_freespace(cxt
, tb
, lastplusoff
, pa
->start
, cont
);
554 last
= fdisk_partition_get_end(pa
);
557 /* free-space remaining in extended partition */
558 x
= fdisk_partition_get_start(cont
) + fdisk_partition_get_size(cont
) - 1;
559 lastplusoff
= last
+ cxt
->first_lba
;
560 if (lastplusoff
< x
&& x
- lastplusoff
> grain
) {
561 DBG(TAB
, ul_debugobj(tb
, "add remaining space in container 0x%p", cont
));
562 rc
= table_add_freespace(cxt
, tb
, lastplusoff
, x
, cont
);
566 DBG(TAB
, ul_debugobj(tb
, "analyze container 0x%p DONE [rc=%d]", cont
, rc
));
572 * fdisk_get_freespaces
573 * @cxt: fdisk context
576 * This function adds freespace (described by fdisk_partition) to @table, it
577 * allocates a new table if the @table points to NULL.
579 * Note that free space smaller than grain (see fdisk_get_grain_size()) is
582 * Returns: 0 on success, otherwise, a corresponding error.
584 int fdisk_get_freespaces(struct fdisk_context
*cxt
, struct fdisk_table
**tb
)
588 fdisk_sector_t last
, grain
;
589 struct fdisk_table
*parts
= NULL
;
590 struct fdisk_partition
*pa
;
591 struct fdisk_iter itr
;
593 DBG(CXT
, ul_debugobj(cxt
, "-- get freespace --"));
595 if (!cxt
|| !cxt
->label
|| !tb
)
597 if (!*tb
&& !(*tb
= fdisk_new_table()))
600 rc
= fdisk_get_partitions(cxt
, &parts
);
604 fdisk_table_sort_partitions(parts
, fdisk_partition_cmp_start
);
605 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
606 last
= cxt
->first_lba
;
607 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
609 DBG(CXT
, ul_debugobj(cxt
, "initialized: last=%ju, grain=%ju",
610 (uintmax_t)last
, (uintmax_t)grain
));
612 /* analyze gaps between partitions */
613 while (rc
== 0 && fdisk_table_next_partition(parts
, &itr
, &pa
) == 0) {
615 DBG(CXT
, ul_debugobj(cxt
, "partno=%zu, start=%ju",
616 pa
->partno
, (uintmax_t)pa
->start
));
618 if (!pa
->used
|| pa
->wholedisk
|| fdisk_partition_is_nested(pa
)
619 || !fdisk_partition_has_start(pa
))
621 DBG(CXT
, ul_debugobj(cxt
, "freespace analyze: partno=%zu, start=%ju, end=%ju",
623 (uintmax_t) fdisk_partition_get_start(pa
),
624 (uintmax_t) fdisk_partition_get_end(pa
)));
626 /* We ignore small free spaces (smaller than grain) to keep partitions
627 * aligned, the exception is space before the first partition when
628 * cxt->first_lba is aligned. */
629 if (last
+ grain
< pa
->start
631 (fdisk_align_lba(cxt
, last
, FDISK_ALIGN_UP
) <
633 rc
= table_add_freespace(cxt
, *tb
,
634 last
+ (nparts
== 0 ? 0 : 1),
635 pa
->start
- 1, NULL
);
637 /* add gaps between logical partitions */
638 if (fdisk_partition_is_container(pa
))
639 rc
= check_container_freespace(cxt
, parts
, *tb
, pa
);
641 if (fdisk_partition_has_end(pa
)) {
642 fdisk_sector_t pa_end
= fdisk_partition_get_end(pa
);
644 last
= fdisk_partition_get_end(pa
);
649 /* add free-space behind last partition to the end of the table (so
650 * don't use table_add_freespace()) */
651 if (rc
== 0 && last
+ grain
< cxt
->last_lba
- 1) {
652 DBG(CXT
, ul_debugobj(cxt
, "freespace behind last partition detected"));
653 rc
= new_freespace(cxt
,
654 last
+ (last
> cxt
->first_lba
|| nparts
? 1 : 0),
655 cxt
->last_lba
, NULL
, &pa
);
657 fdisk_table_add_partition(*tb
, pa
);
658 fdisk_unref_partition(pa
);
663 fdisk_unref_table(parts
);
665 DBG(CXT
, ul_debugobj(cxt
, "get freespace DONE [rc=%d]", rc
));
670 * fdisk_table_wrong_order:
673 * Returns: 1 of the table is not in disk order
675 int fdisk_table_wrong_order(struct fdisk_table
*tb
)
677 struct fdisk_partition
*pa
;
678 struct fdisk_iter itr
;
679 fdisk_sector_t last
= 0;
681 DBG(TAB
, ul_debugobj(tb
, "wrong older check"));
683 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
684 while (tb
&& fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
685 if (!fdisk_partition_has_start(pa
) || fdisk_partition_is_wholedisk(pa
))
687 if (pa
->start
< last
)
699 * Add partitions from table @tb to the in-memory disk label. See
700 * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitions
701 * that does not define start (or does not follow the default start)
704 * Returns: 0 on success, <0 on error.
706 int fdisk_apply_table(struct fdisk_context
*cxt
, struct fdisk_table
*tb
)
708 struct fdisk_partition
*pa
;
709 struct fdisk_iter itr
;
715 DBG(TAB
, ul_debugobj(tb
, "applying to context %p", cxt
));
717 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
718 while (tb
&& fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
719 if (!fdisk_partition_has_start(pa
) && !pa
->start_follow_default
)
721 rc
= fdisk_add_partition(cxt
, pa
, NULL
);
729 int fdisk_diff_tables(struct fdisk_table
*a
, struct fdisk_table
*b
,
730 struct fdisk_iter
*itr
,
731 struct fdisk_partition
**res
, int *change
)
733 struct fdisk_partition
*pa
= NULL
, *pb
;
740 DBG(TAB
, ul_debugobj(a
, "table diff [new table=%p]", b
));
742 if (a
&& (itr
->head
== NULL
|| itr
->head
== &a
->parts
)) {
743 DBG(TAB
, ul_debugobj(a
, " scanning old table"));
745 rc
= fdisk_table_next_partition(a
, itr
, &pa
);
748 } while (!fdisk_partition_has_partno(pa
));
752 DBG(TAB
, ul_debugobj(a
, " scanning new table"));
753 if (itr
->head
!= &b
->parts
) {
754 DBG(TAB
, ul_debugobj(a
, " initialize to TAB=%p", b
));
755 fdisk_reset_iter(itr
, FDISK_ITER_FORWARD
);
758 while (fdisk_table_next_partition(b
, itr
, &pb
) == 0) {
759 if (!fdisk_partition_has_partno(pb
))
762 fdisk_table_get_partition_by_partno(a
, pb
->partno
) == NULL
) {
763 DBG(TAB
, ul_debugobj(a
, " #%zu ADDED", pb
->partno
));
764 *change
= FDISK_DIFF_ADDED
;
772 DBG(TAB
, ul_debugobj(a
, "table diff done [rc=%d]", rc
));
773 return rc
; /* error or done */
776 pb
= fdisk_table_get_partition_by_partno(b
, pa
->partno
);
779 DBG(TAB
, ul_debugobj(a
, " #%zu REMOVED", pa
->partno
));
780 *change
= FDISK_DIFF_REMOVED
;
782 } else if (pb
->start
!= pa
->start
) {
783 DBG(TAB
, ul_debugobj(a
, " #%zu MOVED", pb
->partno
));
784 *change
= FDISK_DIFF_MOVED
;
786 } else if (pb
->size
!= pa
->size
) {
787 DBG(TAB
, ul_debugobj(a
, " #%zu RESIZED", pb
->partno
));
788 *change
= FDISK_DIFF_RESIZED
;
791 DBG(TAB
, ul_debugobj(a
, " #%zu UNCHANGED", pb
->partno
));
792 *change
= FDISK_DIFF_UNCHANGED
;