]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/table.c
libfdisk: rewrite freespace code
[thirdparty/util-linux.git] / libfdisk / src / table.c
CommitLineData
b48cdebc
KZ
1
2#include "fdiskP.h"
3
4/**
5 * fdisk_new_table:
6 *
7 * The table is a container for struct fdisk_partition entries. The container
8 * does not have any real connection with label (partition table) and with
9 * real on-disk data.
10 *
11 * Returns: newly allocated table struct.
12 */
13struct fdisk_table *fdisk_new_table(void)
14{
15 struct fdisk_table *tb = NULL;
16
17 tb = calloc(1, sizeof(*tb));
18 if (!tb)
19 return NULL;
20
21 DBG(TAB, dbgprint("alloc"));
22 tb->refcount = 1;
23 INIT_LIST_HEAD(&tb->parts);
24 return tb;
25}
26
27/**
28 * fdisk_reset_table:
29 * @tb: tab pointer
30 *
31 * Removes all entries (filesystems) from the table. The filesystems with zero
32 * reference count will be deallocated.
33 *
34 * Returns: 0 on success or negative number in case of error.
35 */
36int fdisk_reset_table(struct fdisk_table *tb)
37{
38 if (!tb)
39 return -EINVAL;
40
41 DBG(TAB, dbgprint("reset"));
42
43 while (!list_empty(&tb->parts)) {
44 struct fdisk_partition *pa = list_entry(tb->parts.next,
45 struct fdisk_partition, parts);
46 fdisk_table_remove_partition(tb, pa);
47 }
48
04406c0d 49 tb->nents = 0;
b48cdebc
KZ
50 return 0;
51}
52
53/**
54 * fdisk_ref_table:
55 * @tb: table pointer
56 *
57 * Incremparts reference counter.
58 */
59void fdisk_ref_table(struct fdisk_table *tb)
60{
61 if (tb)
62 tb->refcount++;
63}
64
65/**
66 * fdisk_unref_table:
67 * @tb: table pointer
68 *
69 * De-incremparts reference counter, on zero the @tb is automatically
70 * deallocated by fdisk_free_table().
71 */
72void fdisk_unref_table(struct fdisk_table *tb)
73{
74 if (!tb)
75 return;
76
77 tb->refcount--;
78 if (tb->refcount <= 0) {
79 fdisk_reset_table(tb);
80
81 DBG(TAB, dbgprint("free"));
82 free(tb);
83 }
84}
85
86/**
87 * fdisk_table_is_empty:
88 * @tb: pointer to tab
89 *
90 * Returns: 1 if the table is without filesystems, or 0.
91 */
92int fdisk_table_is_empty(struct fdisk_table *tb)
93{
94 assert(tb);
95 return tb == NULL || list_empty(&tb->parts) ? 1 : 0;
96}
97
04406c0d
KZ
98/**
99 * fdisk_table_get_nents:
100 * @tb: pointer to tab
101 *
102 * Returns: number of entries in table.
103 */
104int fdisk_table_get_nents(struct fdisk_table *tb)
105{
106 return tb ? tb->nents : 0;
107}
6c89f750
KZ
108
109/**
110 * fdisk_table_next_partition:
111 * @tb: tab pointer
112 * @itr: iterator
113 * @pa: returns the next tab entry
114 *
115 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
116 *
117 * Example:
118 * <informalexample>
119 * <programlisting>
120 * while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
121 * ...
122 * }
123 * </programlisting>
124 * </informalexample>
125 */
126int fdisk_table_next_partition(
127 struct fdisk_table *tb,
128 struct fdisk_iter *itr,
129 struct fdisk_partition **pa)
130{
131 int rc = 1;
132
133 assert(tb);
134 assert(itr);
135 assert(pa);
136
137 if (!tb || !itr || !pa)
138 return -EINVAL;
139 *pa = NULL;
140
141 if (!itr->head)
142 FDISK_ITER_INIT(itr, &tb->parts);
143 if (itr->p != itr->head) {
144 FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts);
145 rc = 0;
146 }
147
148 return rc;
149}
150
28b6a23c
KZ
151struct fdisk_partition *fdisk_table_get_partition(
152 struct fdisk_table *tb,
153 size_t n)
154{
155 struct fdisk_partition *pa = NULL;
156 struct fdisk_iter itr;
157
158 if (!tb)
159 return NULL;
160
161 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
162
163 while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
164 if (n == 0)
165 return pa;
166 n--;
167 }
168
169 return NULL;
170}
171
b48cdebc
KZ
172/**
173 * fdisk_table_add_partition
174 * @tb: tab pointer
175 * @pa: new entry
176 *
177 * Adds a new entry to table and increment @pa reference counter. Don't forget to
178 * use fdisk_unref_pa() after fdisk_table_add_partition() if you want to keep
179 * the @pa referenced by the table only.
180 *
181 * Returns: 0 on success or negative number in case of error.
182 */
183int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
184{
185 assert(tb);
186 assert(pa);
187
188 if (!tb || !pa)
189 return -EINVAL;
190
191 fdisk_ref_partition(pa);
192 list_add_tail(&pa->parts, &tb->parts);
04406c0d 193 tb->nents++;
b48cdebc 194
81c29a73
KZ
195 DBG(TAB, dbgprint("add entry %p [start=%ju, end=%ju, size=%ju, freespace=%s]",
196 pa, pa->start, pa->end, pa->size,
6c89f750 197 pa->freespace ? "yes" : "no"));
b48cdebc
KZ
198 return 0;
199}
200
2cec7949
KZ
201/* inserts @pa after @poz */
202static int table_insert_partition(
203 struct fdisk_table *tb,
204 struct fdisk_partition *poz,
205 struct fdisk_partition *pa)
206{
207 assert(tb);
208 assert(pa);
209
210 fdisk_ref_partition(pa);
211 if (poz)
212 list_add(&pa->parts, &poz->parts);
213 else
214 list_add(&pa->parts, &tb->parts);
215 tb->nents++;
216
217 DBG(TAB, dbgprint("insert entry %p [start=%ju, end=%ju, size=%ju, freespace=%s]",
218 pa, pa->start, pa->end, pa->size,
219 pa->freespace ? "yes" : "no"));
220 return 0;
221}
222
b48cdebc
KZ
223/**
224 * fdisk_table_remove_partition
225 * @tb: tab pointer
226 * @pa: new entry
227 *
228 * Removes the @pa from the table and de-increment reference counter of the @pa. The
229 * partition with zero reference counter will be deallocated. Don't forget to use
230 * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
231 * to use @pa later.
232 *
233 * Returns: 0 on success or negative number in case of error.
234 */
235int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
236{
237 assert(tb);
238 assert(pa);
239
240 if (!tb || !pa)
241 return -EINVAL;
242
243 DBG(TAB, dbgprint("remove entry %p", pa));
244 list_del(&pa->parts);
245 INIT_LIST_HEAD(&pa->parts);
246
247 fdisk_unref_partition(pa);
04406c0d
KZ
248 tb->nents--;
249
b48cdebc
KZ
250 return 0;
251}
6c89f750 252
2cec7949
KZ
253/**
254 * fdisk_get_partitions
255 * @cxt: fdisk context
256 * @tb: returns table
257 *
258 * This function adds partitions from disklabel to @table, it allocates a new
259 * table if if @table points to NULL.
260 *
261 * Returns 0 on success, otherwise, a corresponding error.
262 */
263int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb)
264{
265 size_t i;
266
267 if (!cxt || !cxt->label || !tb)
268 return -EINVAL;
269 if (!cxt->label->op->get_part)
270 return -ENOSYS;
271
272 DBG(LABEL, dbgprint("get table"));
273
274 if (!*tb && !(*tb = fdisk_new_table()))
275 return -ENOMEM;
276
277 for (i = 0; i < cxt->label->nparts_max; i++) {
278 struct fdisk_partition *pa = NULL;
279
280 if (fdisk_get_partition(cxt, i, &pa) != 0)
281 continue;
282 if (fdisk_partition_is_used(pa))
283 fdisk_table_add_partition(*tb, pa);
284 fdisk_unref_partition(pa);
285 }
286
287 return 0;
288}
289
290int fdisk_table_add_freespace(
81c29a73 291 struct fdisk_context *cxt,
6c89f750
KZ
292 struct fdisk_table *tb,
293 uint64_t start,
2cec7949
KZ
294 uint64_t end,
295 int dosort)
6c89f750
KZ
296{
297 struct fdisk_partition *pa = fdisk_new_partition();
2cec7949 298 int rc = 0;
6c89f750
KZ
299
300 assert(tb);
301
2cec7949
KZ
302 if (!pa)
303 return -ENOMEM;
6c89f750 304 pa->freespace = 1;
81c29a73 305 pa->start = fdisk_align_lba_in_range(cxt, start, start, end);
6c89f750
KZ
306 pa->end = end;
307 pa->size = pa->end - pa->start + 1ULL;
308
2cec7949
KZ
309 if (!dosort)
310 rc = fdisk_table_add_partition(tb, pa);
311 else {
312 struct fdisk_partition *x, *best = NULL;
313 struct fdisk_iter itr;
314
315 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
316 while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
317 if (x->end < pa->start && (!best || best->end < x->end))
318 best = x;
319 }
320 rc = table_insert_partition(tb, best, pa);
321 }
6c89f750
KZ
322 fdisk_unref_partition(pa);
323 return rc;
324}
2cec7949
KZ
325
326
6c89f750 327/**
2cec7949 328 * fdisk_get_freespaces
6c89f750 329 * @cxt: fdisk context
2cec7949 330 * @tb: returns table
6c89f750 331 *
2cec7949
KZ
332 * This function adds freespace (described by fdisk_partition) to @table, it
333 * allocates a new table if if @table points to NULL.
334
6c89f750
KZ
335 * Returns 0 on success, otherwise, a corresponding error.
336 */
2cec7949 337int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb)
6c89f750 338{
2cec7949 339 int dosort, rc = 0;
6c89f750
KZ
340 size_t i;
341 uint64_t last, grain;
342
2cec7949
KZ
343 DBG(LABEL, dbgprint("get freespace"));
344
6c89f750
KZ
345 if (!cxt || !cxt->label || !tb)
346 return -EINVAL;
2cec7949
KZ
347 if (!*tb && !(*tb = fdisk_new_table()))
348 return -ENOMEM;
6c89f750 349
2cec7949
KZ
350 /* label specific way */
351 if (cxt->label->op->get_freespace)
352 return cxt->label->op->get_freespace(cxt, *tb);
6c89f750 353
2cec7949
KZ
354 /* generic way -- check for gaps betten partitions */
355 dosort = !fdisk_table_is_empty(*tb);
6c89f750
KZ
356 last = cxt->first_lba;
357 grain = cxt->grain / cxt->sector_size;
358
2cec7949
KZ
359 for (i = 0; rc == 0 && i < cxt->label->nparts_max; i++) {
360 struct fdisk_partition *pa = NULL;
361
6c89f750
KZ
362 if (fdisk_get_partition(cxt, i, &pa))
363 continue;
2cec7949
KZ
364 if (!fdisk_partition_is_used(pa)) {
365 fdisk_unref_partition(pa);
6c89f750 366 continue;
2cec7949 367 }
6c89f750
KZ
368
369 /* add free-space (before partition) to the list */
2cec7949
KZ
370 if (last + grain < pa->start) {
371 rc = fdisk_table_add_freespace(cxt, *tb,
6c89f750 372 last + (last > cxt->first_lba ? 1 : 0),
2cec7949
KZ
373 pa->start - 1,
374 dosort);
6c89f750 375 }
2cec7949 376
6c89f750 377 last = pa->end;
6c89f750 378 fdisk_unref_partition(pa);
6c89f750
KZ
379 }
380
381 /* add free-space (behind last partition) to the list */
2cec7949
KZ
382 if (rc == 0 && last + grain < cxt->total_sectors - 1)
383 rc = fdisk_table_add_freespace(cxt, *tb,
6c89f750 384 last + (last > cxt->first_lba ? 1 : 0),
2cec7949
KZ
385 cxt->last_lba,
386 dosort);
387 return rc;
6c89f750
KZ
388}
389
390/**
391 * fdisk_table_to_string
392 * @tb: table
393 * @cxt: fdisk context
394 * @cols: array with wanted FDISK_COL_* columns
395 * @ncols: number of items in the cols array
849968b9 396 * @data: returns table as a newlly allocated string or NULL for empty PT
6c89f750
KZ
397 *
398 * If no @cols are specified then the default is printed (see
399 * fdisk_get_columns() for the default columns).
400
401 * Returns 0 on success, otherwise, a corresponding error.
402 */
403int fdisk_table_to_string(struct fdisk_table *tb,
404 struct fdisk_context *cxt,
405 int *cols,
406 size_t ncols,
407 char **data)
408{
409 int *org_cols = cols, rc = 0;
410 struct tt *tt = NULL;
411 const struct fdisk_column *col;
412 struct fdisk_partition *pa = NULL;
413 struct fdisk_iter itr;
414 size_t j;
415
416 if (!cxt || !tb || !data)
417 return -EINVAL;
418
419 DBG(TAB, dbgprint("generate string"));
849968b9
KZ
420 *data = NULL;
421
422 if (!fdisk_table_get_nents(tb))
423 return 0;
6c89f750
KZ
424
425 if (!cols || !ncols) {
426 rc = fdisk_get_columns(cxt, 0, &cols, &ncols);
427 if (rc)
428 return rc;
429 }
430
431 tt = tt_new_table(TT_FL_FREEDATA);
432 if (!tt) {
433 rc = -ENOMEM;
434 goto done;
435 }
436
437 /* define columns */
438 for (j = 0; j < ncols; j++) {
439 col = fdisk_label_get_column(cxt->label, cols[j]);
d0059a24
KZ
440 if (col)
441 tt_define_column(tt, col->name, col->width, col->tt_flags);
6c89f750
KZ
442 }
443
444 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
445
446 /* convert partition to string and add to tt */
447 while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
448 struct tt_line *ln = tt_add_line(tt, NULL);
449 if (!ln) {
450 rc = -ENOMEM;
451 goto done;
452 }
453
454 DBG(TAB, dbgprint(" string from part #%zu", pa->partno + 1));
455
456 /* set data for the columns */
457 for (j = 0; j < ncols; j++) {
6c89f750 458 char *cdata = NULL;
6c89f750
KZ
459
460 col = fdisk_label_get_column(cxt->label, cols[j]);
461 if (!col)
462 continue;
9f7fdeaf 463 if (fdisk_partition_to_string(pa, cxt, col->id, &cdata))
6c89f750
KZ
464 continue;
465 tt_line_set_data(ln, j, cdata);
466 }
467 }
468
469 rc = 0;
6c89f750
KZ
470 if (!tt_is_empty(tt))
471 rc = tt_print_table_to_string(tt, data);
472 else
473 DBG(TAB, dbgprint("tt empty"));
474done:
475 if (org_cols != cols)
476 free(cols);
477 tt_free_table(tt);
478 return rc;
479}