2 #include <libsmartcols.h>
8 * The table is a container for struct fdisk_partition entries. The container
9 * does not have any real connection with label (partition table) and with
12 * Returns: newly allocated table struct.
14 struct fdisk_table
*fdisk_new_table(void)
16 struct fdisk_table
*tb
= NULL
;
18 tb
= calloc(1, sizeof(*tb
));
22 DBG(TAB
, ul_debugobj(tb
, "alloc"));
24 INIT_LIST_HEAD(&tb
->parts
);
32 * Removes all entries (filesystems) from the table. The filesystems with zero
33 * reference count will be deallocated.
35 * Returns: 0 on success or negative number in case of error.
37 int fdisk_reset_table(struct fdisk_table
*tb
)
42 DBG(TAB
, ul_debugobj(tb
, "reset"));
44 while (!list_empty(&tb
->parts
)) {
45 struct fdisk_partition
*pa
= list_entry(tb
->parts
.next
,
46 struct fdisk_partition
, parts
);
47 fdisk_table_remove_partition(tb
, pa
);
58 * Incremparts reference counter.
60 void fdisk_ref_table(struct fdisk_table
*tb
)
70 * De-incremparts reference counter, on zero the @tb is automatically
71 * deallocated by fdisk_free_table().
73 void fdisk_unref_table(struct fdisk_table
*tb
)
79 if (tb
->refcount
<= 0) {
80 fdisk_reset_table(tb
);
82 DBG(TAB
, ul_debugobj(tb
, "free"));
88 * fdisk_table_is_empty:
91 * Returns: 1 if the table is without filesystems, or 0.
93 int fdisk_table_is_empty(struct fdisk_table
*tb
)
96 return tb
== NULL
|| list_empty(&tb
->parts
) ? 1 : 0;
100 * fdisk_table_get_nents:
101 * @tb: pointer to tab
103 * Returns: number of entries in table.
105 int fdisk_table_get_nents(struct fdisk_table
*tb
)
107 return tb
? tb
->nents
: 0;
111 * fdisk_table_next_partition:
114 * @pa: returns the next tab entry
116 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
121 * while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
127 int fdisk_table_next_partition(
128 struct fdisk_table
*tb
,
129 struct fdisk_iter
*itr
,
130 struct fdisk_partition
**pa
)
138 if (!tb
|| !itr
|| !pa
)
143 FDISK_ITER_INIT(itr
, &tb
->parts
);
144 if (itr
->p
!= itr
->head
) {
145 FDISK_ITER_ITERATE(itr
, *pa
, struct fdisk_partition
, parts
);
152 struct fdisk_partition
*fdisk_table_get_partition(
153 struct fdisk_table
*tb
,
156 struct fdisk_partition
*pa
= NULL
;
157 struct fdisk_iter itr
;
162 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
164 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
174 * fdisk_table_add_partition
178 * Adds a new entry to table and increment @pa reference counter. Don't forget to
179 * use fdisk_unref_pa() after fdisk_table_add_partition() if you want to keep
180 * the @pa referenced by the table only.
182 * Returns: 0 on success or negative number in case of error.
184 int fdisk_table_add_partition(struct fdisk_table
*tb
, struct fdisk_partition
*pa
)
192 fdisk_ref_partition(pa
);
193 list_add_tail(&pa
->parts
, &tb
->parts
);
196 DBG(TAB
, ul_debugobj(tb
, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
197 pa
, pa
->start
, pa
->end
, pa
->size
,
198 fdisk_partition_is_freespace(pa
) ? "freespace" : "",
199 fdisk_partition_is_nested(pa
) ? "nested" : "",
200 fdisk_partition_is_container(pa
) ? "container" : "primary"));
204 /* inserts @pa after @poz */
205 static int table_insert_partition(
206 struct fdisk_table
*tb
,
207 struct fdisk_partition
*poz
,
208 struct fdisk_partition
*pa
)
213 fdisk_ref_partition(pa
);
215 list_add(&pa
->parts
, &poz
->parts
);
217 list_add(&pa
->parts
, &tb
->parts
);
220 DBG(TAB
, ul_debugobj(tb
, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
221 pa
, poz
? poz
: NULL
, pa
->start
, pa
->end
, pa
->size
,
222 fdisk_partition_is_freespace(pa
) ? "freespace" : "",
223 fdisk_partition_is_nested(pa
) ? "nested" : "",
224 fdisk_partition_is_container(pa
) ? "container" : "primary"));
229 * fdisk_table_remove_partition
233 * Removes the @pa from the table and de-increment reference counter of the @pa. The
234 * partition with zero reference counter will be deallocated. Don't forget to use
235 * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
238 * Returns: 0 on success or negative number in case of error.
240 int fdisk_table_remove_partition(struct fdisk_table
*tb
, struct fdisk_partition
*pa
)
248 DBG(TAB
, ul_debugobj(tb
, "remove entry %p", pa
));
249 list_del(&pa
->parts
);
250 INIT_LIST_HEAD(&pa
->parts
);
252 fdisk_unref_partition(pa
);
259 * fdisk_get_partitions
260 * @cxt: fdisk context
263 * This function adds partitions from disklabel to @table, it allocates a new
264 * table if if @table points to NULL.
266 * Returns 0 on success, otherwise, a corresponding error.
268 int fdisk_get_partitions(struct fdisk_context
*cxt
, struct fdisk_table
**tb
)
272 if (!cxt
|| !cxt
->label
|| !tb
)
274 if (!cxt
->label
->op
->get_part
)
277 DBG(CXT
, ul_debugobj(cxt
, "get table"));
279 if (!*tb
&& !(*tb
= fdisk_new_table()))
282 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
283 struct fdisk_partition
*pa
= NULL
;
285 if (fdisk_get_partition(cxt
, i
, &pa
) != 0)
287 if (fdisk_partition_is_used(pa
))
288 fdisk_table_add_partition(*tb
, pa
);
289 fdisk_unref_partition(pa
);
295 int fdisk_dump_table(struct fdisk_table
*tb
, FILE *f
)
297 struct fdisk_partition
*pa
;
298 struct fdisk_iter itr
;
304 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
306 fprintf(f
, "--table--%p\n", tb
);
307 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
308 fprintf(f
, "%d: ", i
++);
309 fdisk_dump_partition(pa
, f
);
316 typedef int (*fdisk_partcmp_t
)(struct fdisk_partition
*, struct fdisk_partition
*);
318 static int cmp_parts_wrapper(struct list_head
*a
, struct list_head
*b
, void *data
)
320 struct fdisk_partition
*pa
= list_entry(a
, struct fdisk_partition
, parts
),
321 *pb
= list_entry(b
, struct fdisk_partition
, parts
);
323 fdisk_partcmp_t cmp
= (fdisk_partcmp_t
) data
;
328 int fdisk_table_sort_partitions(struct fdisk_table
*tb
,
329 int (*cmp
)(struct fdisk_partition
*,
330 struct fdisk_partition
*))
335 list_sort(&tb
->parts
, cmp_parts_wrapper
, (void *) cmp
);
339 /* allocates a new freespace description */
340 static int new_freespace(struct fdisk_context
*cxt
,
343 struct fdisk_partition
*parent
,
344 struct fdisk_partition
**pa
)
353 *pa
= fdisk_new_partition();
357 (*pa
)->freespace
= 1;
358 (*pa
)->start
= fdisk_align_lba_in_range(cxt
, start
, start
, end
);
360 (*pa
)->size
= (*pa
)->end
- (*pa
)->start
+ 1ULL;
363 (*pa
)->parent_partno
= parent
->partno
;
367 /* add freespace description to the right place within @tb */
368 static int table_add_freespace(
369 struct fdisk_context
*cxt
,
370 struct fdisk_table
*tb
,
373 struct fdisk_partition
*parent
)
375 struct fdisk_partition
*pa
, *x
, *real_parent
= NULL
, *best
= NULL
;
376 struct fdisk_iter itr
;
381 rc
= new_freespace(cxt
, start
, end
, parent
, &pa
);
386 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
388 while (fdisk_table_next_partition(tb
, &itr
, &x
) == 0) {
389 if (x
->partno
== parent
->partno
) {
395 DBG(TAB
, ul_debugobj(tb
, "not found freespace parent (partno=%ju)",
397 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
401 while (fdisk_table_next_partition(tb
, &itr
, &x
) == 0) {
402 if (x
->end
< pa
->start
&& (!best
|| best
->end
< x
->end
))
406 if (!best
&& real_parent
)
408 rc
= table_insert_partition(tb
, best
, pa
);
410 fdisk_unref_partition(pa
);
414 /* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
415 * that @parts has to be sorted by partition starts */
416 static int check_container_freespace(struct fdisk_context
*cxt
,
417 struct fdisk_table
*parts
,
418 struct fdisk_table
*tb
,
419 struct fdisk_partition
*cont
)
421 struct fdisk_iter itr
;
422 struct fdisk_partition
*pa
;
423 uint64_t x
, last
, grain
;
424 uint64_t lastplusoff
;
432 last
= fdisk_partition_get_start(cont
);
433 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
434 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
436 while (fdisk_table_next_partition(parts
, &itr
, &pa
) == 0) {
437 if (!pa
->used
|| !fdisk_partition_is_nested(pa
))
440 lastplusoff
= last
+ cxt
->first_lba
;
441 if (pa
->start
> lastplusoff
&& pa
->start
- lastplusoff
> grain
)
442 rc
= table_add_freespace(cxt
, tb
, lastplusoff
, pa
->start
, cont
);
448 /* free-space remaining in extended partition */
449 x
= fdisk_partition_get_start(cont
) + fdisk_partition_get_size(cont
) - 1;
450 lastplusoff
= last
+ cxt
->first_lba
;
451 if (lastplusoff
< x
&& x
- lastplusoff
> grain
)
452 rc
= table_add_freespace(cxt
, tb
, lastplusoff
, x
, cont
);
458 * fdisk_get_freespaces
459 * @cxt: fdisk context
462 * This function adds freespace (described by fdisk_partition) to @table, it
463 * allocates a new table if the @table points to NULL.
465 * Note that free space smaller than grain (see fdisk_topology_get_grain()) is
468 * Returns 0 on success, otherwise, a corresponding error.
470 int fdisk_get_freespaces(struct fdisk_context
*cxt
, struct fdisk_table
**tb
)
473 uint64_t last
, grain
;
474 struct fdisk_table
*parts
= NULL
;
475 struct fdisk_partition
*pa
;
476 struct fdisk_iter itr
;
478 DBG(CXT
, ul_debugobj(cxt
, "get freespace"));
480 if (!cxt
|| !cxt
->label
|| !tb
)
482 if (!*tb
&& !(*tb
= fdisk_new_table()))
485 rc
= fdisk_get_partitions(cxt
, &parts
);
488 fdisk_table_sort_partitions(parts
, fdisk_partition_cmp_start
);
489 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
490 last
= cxt
->first_lba
;
491 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
493 /* analyze gaps between partitions */
494 while (rc
== 0 && fdisk_table_next_partition(parts
, &itr
, &pa
) == 0) {
495 if (!pa
->used
|| pa
->wholedisk
|| fdisk_partition_is_nested(pa
))
497 DBG(CXT
, ul_debugobj(cxt
, "freespace analyze: partno=%zu, start=%ju, end=%ju",
498 pa
->partno
, pa
->start
, pa
->end
));
499 if (last
+ grain
<= pa
->start
) {
500 rc
= table_add_freespace(cxt
, *tb
,
501 last
+ (last
> cxt
->first_lba
? 1 : 0),
502 pa
->start
- 1, NULL
);
504 /* add gaps between logical partitions */
505 if (fdisk_partition_is_container(pa
))
506 rc
= check_container_freespace(cxt
, parts
, *tb
, pa
);
510 /* add free-space behind last partition to the end of the table (so
511 * don't use table_add_freespace()) */
512 if (rc
== 0 && last
+ grain
< cxt
->total_sectors
- 1) {
513 rc
= new_freespace(cxt
,
514 last
+ (last
> cxt
->first_lba
? 1 : 0),
515 cxt
->last_lba
, NULL
, &pa
);
517 fdisk_table_add_partition(*tb
, pa
);
518 fdisk_unref_partition(pa
);
522 DBG(LABEL
, fdisk_dump_table(*tb
, stderr
));
524 fdisk_unref_table(parts
);
529 * fdisk_table_to_string
531 * @cxt: fdisk context
532 * @cols: array with wanted FDISK_COL_* columns
533 * @ncols: number of items in the cols array
534 * @data: returns table as a newlly allocated string or NULL for empty PT
536 * If no @cols are specified then the default is printed (see
537 * fdisk_get_columns() for the default columns).
539 * Returns 0 on success, otherwise, a corresponding error.
541 int fdisk_table_to_string(struct fdisk_table
*tb
,
542 struct fdisk_context
*cxt
,
547 int *org_cols
= cols
, rc
= 0;
548 struct libscols_table
*table
= NULL
;
549 const struct fdisk_column
*col
;
550 struct fdisk_partition
*pa
= NULL
;
551 struct fdisk_iter itr
;
554 if (!cxt
|| !tb
|| !data
)
557 DBG(TAB
, ul_debugobj(tb
, "generate string"));
560 if (!fdisk_table_get_nents(tb
))
563 if (!cols
|| !ncols
) {
564 rc
= fdisk_get_columns(cxt
, 0, &cols
, &ncols
);
569 table
= scols_new_table();
576 for (j
= 0; j
< ncols
; j
++) {
577 col
= fdisk_label_get_column(cxt
->label
, cols
[j
]);
579 if (!scols_table_new_column(table
, col
->name
, col
->width
, col
->scols_flags
))
583 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
585 /* convert partition to string and add to table */
586 while (fdisk_table_next_partition(tb
, &itr
, &pa
) == 0) {
587 struct libscols_line
*ln
= scols_table_new_line(table
, NULL
);
593 DBG(TAB
, ul_debugobj(tb
, " string from part #%zu [%p]",
594 pa
->partno
+ 1, pa
));
596 /* set data for the columns */
597 for (j
= 0; j
< ncols
; j
++) {
600 col
= fdisk_label_get_column(cxt
->label
, cols
[j
]);
603 if (fdisk_partition_to_string(pa
, cxt
, col
->id
, &cdata
))
605 scols_line_refer_data(ln
, j
, cdata
);
610 if (!scols_table_is_empty(table
))
611 rc
= scols_print_table_to_string(table
, data
);
613 DBG(TAB
, ul_debugobj(tb
, "table empty"));
615 if (org_cols
!= cols
)
617 scols_unref_table(table
);