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 parititons 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 * Incremparts reference counter.
70 void fdisk_ref_table(struct fdisk_table
*tb
)
80 * De-incremparts 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
)
147 if (!tb
|| !itr
|| !pa
)
152 FDISK_ITER_INIT(itr
, &tb
->parts
);
153 if (itr
->p
!= itr
->head
) {
154 FDISK_ITER_ITERATE(itr
, *pa
, struct fdisk_partition
, parts
);
161 struct fdisk_partition
*fdisk_table_get_partition(
162 struct fdisk_table
*tb
,
165 struct fdisk_partition
*pa
= NULL
;
166 struct fdisk_iter itr
;
171 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
173 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
183 * fdisk_table_add_partition
187 * Adds a new entry to table and increment @pa reference counter. Don't forget to
188 * use fdisk_unref_pa() after fdisk_table_add_partition() if you want to keep
189 * the @pa referenced by the table only.
191 * Returns: 0 on success or negative number in case of error.
193 int fdisk_table_add_partition(struct fdisk_table
*tb
, struct fdisk_partition
*pa
)
201 fdisk_ref_partition(pa
);
202 list_add_tail(&pa
->parts
, &tb
->parts
);
205 DBG(TAB
, ul_debugobj(tb
, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
207 (uintmax_t) fdisk_partition_get_start(pa
),
208 (uintmax_t) fdisk_partition_get_end(pa
),
209 (uintmax_t) fdisk_partition_get_size(pa
),
210 fdisk_partition_is_freespace(pa
) ? "freespace" : "",
211 fdisk_partition_is_nested(pa
) ? "nested" : "",
212 fdisk_partition_is_container(pa
) ? "container" : "primary"));
216 /* inserts @pa after @poz */
217 static int table_insert_partition(
218 struct fdisk_table
*tb
,
219 struct fdisk_partition
*poz
,
220 struct fdisk_partition
*pa
)
225 fdisk_ref_partition(pa
);
227 list_add(&pa
->parts
, &poz
->parts
);
229 list_add(&pa
->parts
, &tb
->parts
);
232 DBG(TAB
, ul_debugobj(tb
, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
233 pa
, poz
? poz
: NULL
,
234 (uintmax_t) fdisk_partition_get_start(pa
),
235 (uintmax_t) fdisk_partition_get_end(pa
),
236 (uintmax_t) fdisk_partition_get_size(pa
),
237 fdisk_partition_is_freespace(pa
) ? "freespace" : "",
238 fdisk_partition_is_nested(pa
) ? "nested" : "",
239 fdisk_partition_is_container(pa
) ? "container" : ""));
244 * fdisk_table_remove_partition
248 * Removes the @pa from the table and de-increment reference counter of the @pa. The
249 * partition with zero reference counter will be deallocated. Don't forget to use
250 * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
253 * Returns: 0 on success or negative number in case of error.
255 int fdisk_table_remove_partition(struct fdisk_table
*tb
, struct fdisk_partition
*pa
)
263 DBG(TAB
, ul_debugobj(tb
, "remove entry %p", pa
));
264 list_del(&pa
->parts
);
265 INIT_LIST_HEAD(&pa
->parts
);
267 fdisk_unref_partition(pa
);
274 * fdisk_get_partitions
275 * @cxt: fdisk context
278 * This function adds partitions from disklabel to @table, it allocates a new
279 * table if if @table points to NULL.
281 * Returns: 0 on success, otherwise, a corresponding error.
283 int fdisk_get_partitions(struct fdisk_context
*cxt
, struct fdisk_table
**tb
)
287 if (!cxt
|| !cxt
->label
|| !tb
)
289 if (!cxt
->label
->op
->get_part
)
292 DBG(CXT
, ul_debugobj(cxt
, "get table"));
294 if (!*tb
&& !(*tb
= fdisk_new_table()))
297 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
298 struct fdisk_partition
*pa
= NULL
;
300 if (fdisk_get_partition(cxt
, i
, &pa
) != 0)
302 if (fdisk_partition_is_used(pa
))
303 fdisk_table_add_partition(*tb
, pa
);
304 fdisk_unref_partition(pa
);
310 static void debug_print_table(struct fdisk_table
*tb
)
312 struct fdisk_iter itr
;
313 struct fdisk_partition
*pa
;
315 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
316 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0)
317 ul_debugobj(tb
, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju] ",
319 (uintmax_t) fdisk_partition_get_start(pa
),
320 (uintmax_t) fdisk_partition_get_end(pa
),
321 (uintmax_t) fdisk_partition_get_size(pa
));
326 typedef int (*fdisk_partcmp_t
)(struct fdisk_partition
*, struct fdisk_partition
*);
328 static int cmp_parts_wrapper(struct list_head
*a
, struct list_head
*b
, void *data
)
330 struct fdisk_partition
*pa
= list_entry(a
, struct fdisk_partition
, parts
),
331 *pb
= list_entry(b
, struct fdisk_partition
, parts
);
333 fdisk_partcmp_t cmp
= (fdisk_partcmp_t
) data
;
340 * fdisk_table_sort_partitions:
342 * @cmp: compare function
344 * Sort partition in the table.
346 * Returns: 0 on success, <0 on error.
348 int fdisk_table_sort_partitions(struct fdisk_table
*tb
,
349 int (*cmp
)(struct fdisk_partition
*,
350 struct fdisk_partition
*))
355 DBG(TAB
, ul_debugobj(tb
, "Before sort:"));
356 ON_DBG(TAB
, debug_print_table(tb
));
358 list_sort(&tb
->parts
, cmp_parts_wrapper
, (void *) cmp
);
360 DBG(TAB
, ul_debugobj(tb
, "After sort:"));
361 ON_DBG(TAB
, debug_print_table(tb
));
366 /* allocates a new freespace description */
367 static int new_freespace(struct fdisk_context
*cxt
,
368 fdisk_sector_t start
,
370 struct fdisk_partition
*parent
,
371 struct fdisk_partition
**pa
)
380 *pa
= fdisk_new_partition();
388 (*pa
)->freespace
= 1;
389 (*pa
)->start
= fdisk_align_lba_in_range(cxt
, start
, start
, end
);
390 (*pa
)->size
= end
- (*pa
)->start
+ 1ULL;
393 (*pa
)->parent_partno
= parent
->partno
;
397 /* add freespace description to the right place within @tb */
398 static int table_add_freespace(
399 struct fdisk_context
*cxt
,
400 struct fdisk_table
*tb
,
401 fdisk_sector_t start
,
403 struct fdisk_partition
*parent
)
405 struct fdisk_partition
*pa
, *x
, *real_parent
= NULL
, *best
= NULL
;
406 struct fdisk_iter itr
;
411 rc
= new_freespace(cxt
, start
, end
, parent
, &pa
);
417 assert(fdisk_partition_has_start(pa
));
418 assert(fdisk_partition_has_end(pa
));
420 DBG(TAB
, ul_debugobj(tb
, "adding freespace"));
422 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
423 if (parent
&& fdisk_partition_has_partno(parent
)) {
424 while (fdisk_table_next_partition(tb
, &itr
, &x
) == 0) {
425 if (!fdisk_partition_has_partno(x
))
427 if (x
->partno
== parent
->partno
) {
433 DBG(TAB
, ul_debugobj(tb
, "not found freespace parent (partno=%zu)",
435 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
439 while (fdisk_table_next_partition(tb
, &itr
, &x
) == 0) {
440 fdisk_sector_t the_end
, best_end
= 0;
442 if (!fdisk_partition_has_end(x
))
445 the_end
= fdisk_partition_get_end(x
);
447 best_end
= fdisk_partition_get_end(best
);
449 if (the_end
< pa
->start
&& (!best
|| best_end
< the_end
))
453 if (!best
&& real_parent
)
455 rc
= table_insert_partition(tb
, best
, pa
);
457 fdisk_unref_partition(pa
);
459 DBG(TAB
, ul_debugobj(tb
, "adding freespace DONE [rc=%d]", rc
));
463 /* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
464 * that @parts has to be sorted by partition starts */
465 static int check_container_freespace(struct fdisk_context
*cxt
,
466 struct fdisk_table
*parts
,
467 struct fdisk_table
*tb
,
468 struct fdisk_partition
*cont
)
470 struct fdisk_iter itr
;
471 struct fdisk_partition
*pa
;
472 fdisk_sector_t x
, last
, grain
, lastplusoff
;
479 assert(fdisk_partition_has_start(cont
));
481 DBG(TAB
, ul_debugobj(tb
, "analyze container 0x%p", cont
));
483 last
= fdisk_partition_get_start(cont
);
484 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
485 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
487 DBG(CXT
, ul_debugobj(cxt
, "initialized: last=%ju, grain=%ju", last
, grain
));
489 while (fdisk_table_next_partition(parts
, &itr
, &pa
) == 0) {
491 DBG(CXT
, ul_debugobj(cxt
, "partno=%zu, start=%ju", pa
->partno
, pa
->start
));
493 if (!pa
->used
|| !fdisk_partition_is_nested(pa
)
494 || !fdisk_partition_has_start(pa
))
497 DBG(CXT
, ul_debugobj(cxt
, "freespace container analyze: partno=%zu, start=%ju, end=%ju",
499 (uintmax_t) fdisk_partition_get_start(pa
),
500 (uintmax_t) fdisk_partition_get_end(pa
)));
502 lastplusoff
= last
+ cxt
->first_lba
;
503 if (pa
->start
> lastplusoff
&& pa
->start
- lastplusoff
> grain
)
504 rc
= table_add_freespace(cxt
, tb
, lastplusoff
, pa
->start
, cont
);
507 last
= fdisk_partition_get_end(pa
);
510 /* free-space remaining in extended partition */
511 x
= fdisk_partition_get_start(cont
) + fdisk_partition_get_size(cont
) - 1;
512 lastplusoff
= last
+ cxt
->first_lba
;
513 if (lastplusoff
< x
&& x
- lastplusoff
> grain
) {
514 DBG(TAB
, ul_debugobj(tb
, "add remaining space in container 0x%p", cont
));
515 rc
= table_add_freespace(cxt
, tb
, lastplusoff
, x
, cont
);
519 DBG(TAB
, ul_debugobj(tb
, "analyze container 0x%p DONE [rc=%d]", cont
, rc
));
525 * fdisk_get_freespaces
526 * @cxt: fdisk context
529 * This function adds freespace (described by fdisk_partition) to @table, it
530 * allocates a new table if the @table points to NULL.
532 * Note that free space smaller than grain (see fdisk_get_grain()) is ignored.
534 * Returns: 0 on success, otherwise, a corresponding error.
536 int fdisk_get_freespaces(struct fdisk_context
*cxt
, struct fdisk_table
**tb
)
539 fdisk_sector_t last
, grain
;
540 struct fdisk_table
*parts
= NULL
;
541 struct fdisk_partition
*pa
;
542 struct fdisk_iter itr
;
544 DBG(CXT
, ul_debugobj(cxt
, "get freespace"));
546 if (!cxt
|| !cxt
->label
|| !tb
)
548 if (!*tb
&& !(*tb
= fdisk_new_table()))
551 rc
= fdisk_get_partitions(cxt
, &parts
);
555 fdisk_table_sort_partitions(parts
, fdisk_partition_cmp_start
);
556 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
557 last
= cxt
->first_lba
;
558 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
560 DBG(CXT
, ul_debugobj(cxt
, "initialized: last=%ju, grain=%ju", last
, grain
));
562 /* analyze gaps between partitions */
563 while (rc
== 0 && fdisk_table_next_partition(parts
, &itr
, &pa
) == 0) {
565 DBG(CXT
, ul_debugobj(cxt
, "partno=%zu, start=%ju", pa
->partno
, pa
->start
));
567 if (!pa
->used
|| pa
->wholedisk
|| fdisk_partition_is_nested(pa
)
568 || !fdisk_partition_has_start(pa
))
570 DBG(CXT
, ul_debugobj(cxt
, "freespace analyze: partno=%zu, start=%ju, end=%ju",
572 (uintmax_t) fdisk_partition_get_start(pa
),
573 (uintmax_t) fdisk_partition_get_end(pa
)));
574 if (last
+ grain
<= pa
->start
) {
575 rc
= table_add_freespace(cxt
, *tb
,
576 last
+ (last
> cxt
->first_lba
? 1 : 0),
577 pa
->start
- 1, NULL
);
579 /* add gaps between logical partitions */
580 if (fdisk_partition_is_container(pa
))
581 rc
= check_container_freespace(cxt
, parts
, *tb
, pa
);
582 last
= fdisk_partition_get_end(pa
);
585 /* add free-space behind last partition to the end of the table (so
586 * don't use table_add_freespace()) */
587 if (rc
== 0 && last
+ grain
< cxt
->total_sectors
- 1) {
588 DBG(CXT
, ul_debugobj(cxt
, "freespace behind last partition detected"));
589 rc
= new_freespace(cxt
,
590 last
+ (last
> cxt
->first_lba
? 1 : 0),
591 cxt
->last_lba
, NULL
, &pa
);
593 fdisk_table_add_partition(*tb
, pa
);
594 fdisk_unref_partition(pa
);
599 fdisk_unref_table(parts
);
601 DBG(CXT
, ul_debugobj(cxt
, "get freespace DONE [rc=%d]", rc
));
606 * fdisk_table_wrong_order:
609 * Returns: 1 of the table is not in disk order
611 int fdisk_table_wrong_order(struct fdisk_table
*tb
)
613 struct fdisk_partition
*pa
;
614 struct fdisk_iter itr
;
615 fdisk_sector_t last
= 0;
617 DBG(TAB
, ul_debugobj(tb
, "wrong older check"));
619 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
620 while (tb
&& fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
621 if (!fdisk_partition_has_start(pa
))
623 if (pa
->start
< last
)
635 * Add partitions from table @tb to the in-memory disk label. See
636 * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitons
637 * that does not define start (or does not follow the default start)
640 * Returns: 0 on success, <0 on error.
642 int fdisk_apply_table(struct fdisk_context
*cxt
, struct fdisk_table
*tb
)
644 struct fdisk_partition
*pa
;
645 struct fdisk_iter itr
;
651 DBG(TAB
, ul_debugobj(tb
, "applying to context %p", cxt
));
653 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
654 while (tb
&& fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
655 if (!fdisk_partition_has_start(pa
) && !pa
->start_follow_default
)
657 rc
= fdisk_add_partition(cxt
, pa
, NULL
);