]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/table.c
libsmartcols: clean up flags usage
[thirdparty/util-linux.git] / libfdisk / src / table.c
1
2 #include <libsmartcols.h>
3 #include "fdiskP.h"
4
5 /**
6 * fdisk_new_table:
7 *
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
10 * real on-disk data.
11 *
12 * Returns: newly allocated table struct.
13 */
14 struct fdisk_table *fdisk_new_table(void)
15 {
16 struct fdisk_table *tb = NULL;
17
18 tb = calloc(1, sizeof(*tb));
19 if (!tb)
20 return NULL;
21
22 DBG(TAB, ul_debugobj(tb, "alloc"));
23 tb->refcount = 1;
24 INIT_LIST_HEAD(&tb->parts);
25 return tb;
26 }
27
28 /**
29 * fdisk_reset_table:
30 * @tb: tab pointer
31 *
32 * Removes all entries (filesystems) from the table. The filesystems with zero
33 * reference count will be deallocated.
34 *
35 * Returns: 0 on success or negative number in case of error.
36 */
37 int fdisk_reset_table(struct fdisk_table *tb)
38 {
39 if (!tb)
40 return -EINVAL;
41
42 DBG(TAB, ul_debugobj(tb, "reset"));
43
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);
48 }
49
50 tb->nents = 0;
51 return 0;
52 }
53
54 /**
55 * fdisk_ref_table:
56 * @tb: table pointer
57 *
58 * Incremparts reference counter.
59 */
60 void fdisk_ref_table(struct fdisk_table *tb)
61 {
62 if (tb)
63 tb->refcount++;
64 }
65
66 /**
67 * fdisk_unref_table:
68 * @tb: table pointer
69 *
70 * De-incremparts reference counter, on zero the @tb is automatically
71 * deallocated by fdisk_free_table().
72 */
73 void fdisk_unref_table(struct fdisk_table *tb)
74 {
75 if (!tb)
76 return;
77
78 tb->refcount--;
79 if (tb->refcount <= 0) {
80 fdisk_reset_table(tb);
81
82 DBG(TAB, ul_debugobj(tb, "free"));
83 free(tb);
84 }
85 }
86
87 /**
88 * fdisk_table_is_empty:
89 * @tb: pointer to tab
90 *
91 * Returns: 1 if the table is without filesystems, or 0.
92 */
93 int fdisk_table_is_empty(struct fdisk_table *tb)
94 {
95 assert(tb);
96 return tb == NULL || list_empty(&tb->parts) ? 1 : 0;
97 }
98
99 /**
100 * fdisk_table_get_nents:
101 * @tb: pointer to tab
102 *
103 * Returns: number of entries in table.
104 */
105 int fdisk_table_get_nents(struct fdisk_table *tb)
106 {
107 return tb ? tb->nents : 0;
108 }
109
110 /**
111 * fdisk_table_next_partition:
112 * @tb: tab pointer
113 * @itr: iterator
114 * @pa: returns the next tab entry
115 *
116 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
117 *
118 * Example:
119 * <informalexample>
120 * <programlisting>
121 * while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
122 * ...
123 * }
124 * </programlisting>
125 * </informalexample>
126 */
127 int fdisk_table_next_partition(
128 struct fdisk_table *tb,
129 struct fdisk_iter *itr,
130 struct fdisk_partition **pa)
131 {
132 int rc = 1;
133
134 assert(tb);
135 assert(itr);
136 assert(pa);
137
138 if (!tb || !itr || !pa)
139 return -EINVAL;
140 *pa = NULL;
141
142 if (!itr->head)
143 FDISK_ITER_INIT(itr, &tb->parts);
144 if (itr->p != itr->head) {
145 FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts);
146 rc = 0;
147 }
148
149 return rc;
150 }
151
152 struct fdisk_partition *fdisk_table_get_partition(
153 struct fdisk_table *tb,
154 size_t n)
155 {
156 struct fdisk_partition *pa = NULL;
157 struct fdisk_iter itr;
158
159 if (!tb)
160 return NULL;
161
162 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
163
164 while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
165 if (n == 0)
166 return pa;
167 n--;
168 }
169
170 return NULL;
171 }
172
173 /**
174 * fdisk_table_add_partition
175 * @tb: tab pointer
176 * @pa: new entry
177 *
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.
181 *
182 * Returns: 0 on success or negative number in case of error.
183 */
184 int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
185 {
186 assert(tb);
187 assert(pa);
188
189 if (!tb || !pa)
190 return -EINVAL;
191
192 fdisk_ref_partition(pa);
193 list_add_tail(&pa->parts, &tb->parts);
194 tb->nents++;
195
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"));
201 return 0;
202 }
203
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)
209 {
210 assert(tb);
211 assert(pa);
212
213 fdisk_ref_partition(pa);
214 if (poz)
215 list_add(&pa->parts, &poz->parts);
216 else
217 list_add(&pa->parts, &tb->parts);
218 tb->nents++;
219
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"));
225 return 0;
226 }
227
228 /**
229 * fdisk_table_remove_partition
230 * @tb: tab pointer
231 * @pa: new entry
232 *
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
236 * to use @pa later.
237 *
238 * Returns: 0 on success or negative number in case of error.
239 */
240 int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
241 {
242 assert(tb);
243 assert(pa);
244
245 if (!tb || !pa)
246 return -EINVAL;
247
248 DBG(TAB, ul_debugobj(tb, "remove entry %p", pa));
249 list_del(&pa->parts);
250 INIT_LIST_HEAD(&pa->parts);
251
252 fdisk_unref_partition(pa);
253 tb->nents--;
254
255 return 0;
256 }
257
258 /**
259 * fdisk_get_partitions
260 * @cxt: fdisk context
261 * @tb: returns table
262 *
263 * This function adds partitions from disklabel to @table, it allocates a new
264 * table if if @table points to NULL.
265 *
266 * Returns 0 on success, otherwise, a corresponding error.
267 */
268 int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb)
269 {
270 size_t i;
271
272 if (!cxt || !cxt->label || !tb)
273 return -EINVAL;
274 if (!cxt->label->op->get_part)
275 return -ENOSYS;
276
277 DBG(CXT, ul_debugobj(cxt, "get table"));
278
279 if (!*tb && !(*tb = fdisk_new_table()))
280 return -ENOMEM;
281
282 for (i = 0; i < cxt->label->nparts_max; i++) {
283 struct fdisk_partition *pa = NULL;
284
285 if (fdisk_get_partition(cxt, i, &pa) != 0)
286 continue;
287 if (fdisk_partition_is_used(pa))
288 fdisk_table_add_partition(*tb, pa);
289 fdisk_unref_partition(pa);
290 }
291
292 return 0;
293 }
294
295 int fdisk_dump_table(struct fdisk_table *tb, FILE *f)
296 {
297 struct fdisk_partition *pa;
298 struct fdisk_iter itr;
299 int i = 0;
300
301 assert(tb);
302 assert(f);
303
304 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
305
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);
310 }
311 fputs("-----\n", f);
312 return 0;
313 }
314
315
316 typedef int (*fdisk_partcmp_t)(struct fdisk_partition *, struct fdisk_partition *);
317
318 static int cmp_parts_wrapper(struct list_head *a, struct list_head *b, void *data)
319 {
320 struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts),
321 *pb = list_entry(b, struct fdisk_partition, parts);
322
323 fdisk_partcmp_t cmp = (fdisk_partcmp_t) data;
324
325 return cmp(pa, pb);
326 }
327
328 int fdisk_table_sort_partitions(struct fdisk_table *tb,
329 int (*cmp)(struct fdisk_partition *,
330 struct fdisk_partition *))
331 {
332 if (!tb)
333 return -EINVAL;
334
335 list_sort(&tb->parts, cmp_parts_wrapper, (void *) cmp);
336 return 0;
337 }
338
339 /* allocates a new freespace description */
340 static int new_freespace(struct fdisk_context *cxt,
341 uint64_t start,
342 uint64_t end,
343 struct fdisk_partition *parent,
344 struct fdisk_partition **pa)
345 {
346 assert(cxt);
347 assert(pa);
348
349 *pa = NULL;
350
351 if (start == end)
352 return 0;
353 *pa = fdisk_new_partition();
354 if (!*pa)
355 return -ENOMEM;
356
357 (*pa)->freespace = 1;
358 (*pa)->start = fdisk_align_lba_in_range(cxt, start, start, end);
359 (*pa)->end = end;
360 (*pa)->size = (*pa)->end - (*pa)->start + 1ULL;
361
362 if (parent)
363 (*pa)->parent_partno = parent->partno;
364 return 0;
365 }
366
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,
371 uint64_t start,
372 uint64_t end,
373 struct fdisk_partition *parent)
374 {
375 struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL;
376 struct fdisk_iter itr;
377 int rc = 0;
378
379 assert(tb);
380
381 rc = new_freespace(cxt, start, end, parent, &pa);
382 if (rc)
383 return -ENOMEM;
384 if (!pa)
385 return 0;
386 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
387 if (parent) {
388 while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
389 if (x->partno == parent->partno) {
390 real_parent = x;
391 break;
392 }
393 }
394 if (!real_parent) {
395 DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%ju)",
396 parent->partno));
397 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
398 }
399 }
400
401 while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
402 if (x->end < pa->start && (!best || best->end < x->end))
403 best = x;
404 }
405
406 if (!best && real_parent)
407 best = real_parent;
408 rc = table_insert_partition(tb, best, pa);
409
410 fdisk_unref_partition(pa);
411 return rc;
412 }
413
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)
420 {
421 struct fdisk_iter itr;
422 struct fdisk_partition *pa;
423 uint64_t x, last, grain;
424 uint64_t lastplusoff;
425 int rc = 0;
426
427 assert(cxt);
428 assert(parts);
429 assert(tb);
430 assert(cont);
431
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);
435
436 while (fdisk_table_next_partition(parts, &itr, &pa) == 0) {
437 if (!pa->used || !fdisk_partition_is_nested(pa))
438 continue;
439
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);
443 if (rc)
444 return rc;
445 last = pa->end;
446 }
447
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);
453 return rc;
454 }
455
456
457 /**
458 * fdisk_get_freespaces
459 * @cxt: fdisk context
460 * @tb: returns table
461 *
462 * This function adds freespace (described by fdisk_partition) to @table, it
463 * allocates a new table if the @table points to NULL.
464 *
465 * Note that free space smaller than grain (see fdisk_topology_get_grain()) is
466 * ignored.
467
468 * Returns 0 on success, otherwise, a corresponding error.
469 */
470 int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb)
471 {
472 int rc = 0;
473 uint64_t last, grain;
474 struct fdisk_table *parts = NULL;
475 struct fdisk_partition *pa;
476 struct fdisk_iter itr;
477
478 DBG(CXT, ul_debugobj(cxt, "get freespace"));
479
480 if (!cxt || !cxt->label || !tb)
481 return -EINVAL;
482 if (!*tb && !(*tb = fdisk_new_table()))
483 return -ENOMEM;
484
485 rc = fdisk_get_partitions(cxt, &parts);
486 if (rc)
487 goto done;
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;
492
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))
496 continue;
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);
503 }
504 /* add gaps between logical partitions */
505 if (fdisk_partition_is_container(pa))
506 rc = check_container_freespace(cxt, parts, *tb, pa);
507 last = pa->end;
508 }
509
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);
516 if (pa) {
517 fdisk_table_add_partition(*tb, pa);
518 fdisk_unref_partition(pa);
519 }
520 }
521
522 DBG(LABEL, fdisk_dump_table(*tb, stderr));
523 done:
524 fdisk_unref_table(parts);
525 return rc;
526 }
527
528 /**
529 * fdisk_table_to_string
530 * @tb: table
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
535 *
536 * If no @cols are specified then the default is printed (see
537 * fdisk_get_columns() for the default columns).
538
539 * Returns 0 on success, otherwise, a corresponding error.
540 */
541 int fdisk_table_to_string(struct fdisk_table *tb,
542 struct fdisk_context *cxt,
543 int *cols,
544 size_t ncols,
545 char **data)
546 {
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;
552 size_t j;
553
554 if (!cxt || !tb || !data)
555 return -EINVAL;
556
557 DBG(TAB, ul_debugobj(tb, "generate string"));
558 *data = NULL;
559
560 if (!fdisk_table_get_nents(tb))
561 return 0;
562
563 if (!cols || !ncols) {
564 rc = fdisk_get_columns(cxt, 0, &cols, &ncols);
565 if (rc)
566 return rc;
567 }
568
569 table = scols_new_table();
570 if (!table) {
571 rc = -ENOMEM;
572 goto done;
573 }
574
575 /* define columns */
576 for (j = 0; j < ncols; j++) {
577 col = fdisk_label_get_column(cxt->label, cols[j]);
578 if (col)
579 if (!scols_table_new_column(table, col->name, col->width, col->scols_flags))
580 goto done;
581 }
582
583 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
584
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);
588 if (!ln) {
589 rc = -ENOMEM;
590 goto done;
591 }
592
593 DBG(TAB, ul_debugobj(tb, " string from part #%zu [%p]",
594 pa->partno + 1, pa));
595
596 /* set data for the columns */
597 for (j = 0; j < ncols; j++) {
598 char *cdata = NULL;
599
600 col = fdisk_label_get_column(cxt->label, cols[j]);
601 if (!col)
602 continue;
603 if (fdisk_partition_to_string(pa, cxt, col->id, &cdata))
604 continue;
605 scols_line_refer_data(ln, j, cdata);
606 }
607 }
608
609 rc = 0;
610 if (!scols_table_is_empty(table))
611 rc = scols_print_table_to_string(table, data);
612 else
613 DBG(TAB, ul_debugobj(tb, "table empty"));
614 done:
615 if (org_cols != cols)
616 free(cols);
617 scols_unref_table(table);
618 return rc;
619 }