]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/table.c
libfdisk: make it possible to use zero for size and start
[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
d71bd2f0 21 DBG(TAB, ul_debugobj(tb, "alloc"));
b48cdebc
KZ
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 *
3c0e6b15
KZ
31 * Removes all entries (partitions) from the table. The parititons with zero
32 * reference count will be deallocated. This function does not modify partition
33 * table.
b48cdebc
KZ
34 *
35 * Returns: 0 on success or negative number in case of error.
36 */
37int fdisk_reset_table(struct fdisk_table *tb)
38{
39 if (!tb)
40 return -EINVAL;
41
d71bd2f0 42 DBG(TAB, ul_debugobj(tb, "reset"));
b48cdebc
KZ
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
04406c0d 50 tb->nents = 0;
b48cdebc
KZ
51 return 0;
52}
53
54/**
55 * fdisk_ref_table:
56 * @tb: table pointer
57 *
58 * Incremparts reference counter.
59 */
60void 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
5175ae87 71 * deallocated.
b48cdebc
KZ
72 */
73void 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
d71bd2f0 82 DBG(TAB, ul_debugobj(tb, "free"));
b48cdebc
KZ
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 */
93int fdisk_table_is_empty(struct fdisk_table *tb)
94{
b48cdebc
KZ
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 */
e54b1c6f 104size_t fdisk_table_get_nents(struct fdisk_table *tb)
04406c0d
KZ
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
d71bd2f0 195 DBG(TAB, ul_debugobj(tb, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
e77313a6
KZ
196 pa, pa->start, pa->end, pa->size,
197 fdisk_partition_is_freespace(pa) ? "freespace" : "",
198 fdisk_partition_is_nested(pa) ? "nested" : "",
5139eca7 199 fdisk_partition_is_container(pa) ? "container" : "primary"));
b48cdebc
KZ
200 return 0;
201}
202
2cec7949
KZ
203/* inserts @pa after @poz */
204static int table_insert_partition(
205 struct fdisk_table *tb,
206 struct fdisk_partition *poz,
207 struct fdisk_partition *pa)
208{
209 assert(tb);
210 assert(pa);
211
212 fdisk_ref_partition(pa);
213 if (poz)
214 list_add(&pa->parts, &poz->parts);
215 else
216 list_add(&pa->parts, &tb->parts);
217 tb->nents++;
218
d71bd2f0 219 DBG(TAB, ul_debugobj(tb, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
5139eca7
KZ
220 pa, poz ? poz : NULL, pa->start, pa->end, pa->size,
221 fdisk_partition_is_freespace(pa) ? "freespace" : "",
222 fdisk_partition_is_nested(pa) ? "nested" : "",
223 fdisk_partition_is_container(pa) ? "container" : "primary"));
2cec7949
KZ
224 return 0;
225}
226
b48cdebc
KZ
227/**
228 * fdisk_table_remove_partition
229 * @tb: tab pointer
230 * @pa: new entry
231 *
232 * Removes the @pa from the table and de-increment reference counter of the @pa. The
233 * partition with zero reference counter will be deallocated. Don't forget to use
234 * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
235 * to use @pa later.
236 *
237 * Returns: 0 on success or negative number in case of error.
238 */
239int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
240{
241 assert(tb);
242 assert(pa);
243
244 if (!tb || !pa)
245 return -EINVAL;
246
d71bd2f0 247 DBG(TAB, ul_debugobj(tb, "remove entry %p", pa));
b48cdebc
KZ
248 list_del(&pa->parts);
249 INIT_LIST_HEAD(&pa->parts);
250
251 fdisk_unref_partition(pa);
04406c0d
KZ
252 tb->nents--;
253
b48cdebc
KZ
254 return 0;
255}
6c89f750 256
2cec7949
KZ
257/**
258 * fdisk_get_partitions
259 * @cxt: fdisk context
260 * @tb: returns table
261 *
262 * This function adds partitions from disklabel to @table, it allocates a new
263 * table if if @table points to NULL.
264 *
265 * Returns 0 on success, otherwise, a corresponding error.
266 */
267int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb)
268{
269 size_t i;
270
271 if (!cxt || !cxt->label || !tb)
272 return -EINVAL;
273 if (!cxt->label->op->get_part)
274 return -ENOSYS;
275
d71bd2f0 276 DBG(CXT, ul_debugobj(cxt, "get table"));
2cec7949
KZ
277
278 if (!*tb && !(*tb = fdisk_new_table()))
279 return -ENOMEM;
280
281 for (i = 0; i < cxt->label->nparts_max; i++) {
282 struct fdisk_partition *pa = NULL;
283
284 if (fdisk_get_partition(cxt, i, &pa) != 0)
285 continue;
286 if (fdisk_partition_is_used(pa))
287 fdisk_table_add_partition(*tb, pa);
288 fdisk_unref_partition(pa);
289 }
290
291 return 0;
292}
293
e5c93999
KZ
294typedef int (*fdisk_partcmp_t)(struct fdisk_partition *, struct fdisk_partition *);
295
296static int cmp_parts_wrapper(struct list_head *a, struct list_head *b, void *data)
297{
298 struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts),
299 *pb = list_entry(b, struct fdisk_partition, parts);
300
301 fdisk_partcmp_t cmp = (fdisk_partcmp_t) data;
302
303 return cmp(pa, pb);
304}
305
5175ae87
KZ
306/**
307 * fdisk_table_sort_partitions:
308 * @tb: table
309 * @cmp: compare function
310 *
311 * Sort partition in the table.
312 */
e5c93999
KZ
313int fdisk_table_sort_partitions(struct fdisk_table *tb,
314 int (*cmp)(struct fdisk_partition *,
315 struct fdisk_partition *))
316{
317 if (!tb)
318 return -EINVAL;
319
320 list_sort(&tb->parts, cmp_parts_wrapper, (void *) cmp);
321 return 0;
322}
323
5139eca7
KZ
324/* allocates a new freespace description */
325static int new_freespace(struct fdisk_context *cxt,
326 uint64_t start,
327 uint64_t end,
328 struct fdisk_partition *parent,
329 struct fdisk_partition **pa)
330{
331 assert(cxt);
332 assert(pa);
333
334 *pa = NULL;
335
336 if (start == end)
337 return 0;
338 *pa = fdisk_new_partition();
339 if (!*pa)
340 return -ENOMEM;
341
342 (*pa)->freespace = 1;
343 (*pa)->start = fdisk_align_lba_in_range(cxt, start, start, end);
344 (*pa)->end = end;
345 (*pa)->size = (*pa)->end - (*pa)->start + 1ULL;
346
347 if (parent)
348 (*pa)->parent_partno = parent->partno;
349 return 0;
350}
351
352/* add freespace description to the right place within @tb */
03643931 353static int table_add_freespace(
81c29a73 354 struct fdisk_context *cxt,
6c89f750
KZ
355 struct fdisk_table *tb,
356 uint64_t start,
2cec7949 357 uint64_t end,
bd5e8291 358 struct fdisk_partition *parent)
6c89f750 359{
bd5e8291
KZ
360 struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL;
361 struct fdisk_iter itr;
5139eca7 362 int rc = 0;
6c89f750
KZ
363
364 assert(tb);
365
5139eca7
KZ
366 rc = new_freespace(cxt, start, end, parent, &pa);
367 if (rc)
2cec7949 368 return -ENOMEM;
5139eca7
KZ
369 if (!pa)
370 return 0;
bd5e8291 371 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
bd5e8291 372 if (parent) {
2cec7949 373 while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
bd5e8291
KZ
374 if (x->partno == parent->partno) {
375 real_parent = x;
376 break;
377 }
378 }
379 if (!real_parent) {
73548d6e 380 DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%zu)",
bd5e8291
KZ
381 parent->partno));
382 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
2cec7949 383 }
2cec7949 384 }
bd5e8291
KZ
385
386 while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
387 if (x->end < pa->start && (!best || best->end < x->end))
388 best = x;
389 }
390
391 if (!best && real_parent)
392 best = real_parent;
393 rc = table_insert_partition(tb, best, pa);
394
6c89f750
KZ
395 fdisk_unref_partition(pa);
396 return rc;
397}
2cec7949 398
5139eca7
KZ
399/* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
400 * that @parts has to be sorted by partition starts */
401static int check_container_freespace(struct fdisk_context *cxt,
402 struct fdisk_table *parts,
403 struct fdisk_table *tb,
404 struct fdisk_partition *cont)
405{
406 struct fdisk_iter itr;
407 struct fdisk_partition *pa;
408 uint64_t x, last, grain;
5fd7160e 409 uint64_t lastplusoff;
5139eca7
KZ
410 int rc = 0;
411
412 assert(cxt);
413 assert(parts);
414 assert(tb);
415 assert(cont);
ecf40cda 416 assert(fdisk_partition_has_start(cont));
5139eca7 417
5fd7160e 418 last = fdisk_partition_get_start(cont);
5139eca7
KZ
419 grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
420 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
421
422 while (fdisk_table_next_partition(parts, &itr, &pa) == 0) {
ecf40cda
KZ
423 if (!pa->used || !fdisk_partition_is_nested(pa)
424 || !fdisk_partition_has_start(pa))
5139eca7 425 continue;
5fd7160e
KZ
426
427 lastplusoff = last + cxt->first_lba;
428 if (pa->start > lastplusoff && pa->start - lastplusoff > grain)
429 rc = table_add_freespace(cxt, tb, lastplusoff, pa->start, cont);
5139eca7
KZ
430 if (rc)
431 return rc;
432 last = pa->end;
433 }
434
435 /* free-space remaining in extended partition */
436 x = fdisk_partition_get_start(cont) + fdisk_partition_get_size(cont) - 1;
5fd7160e
KZ
437 lastplusoff = last + cxt->first_lba;
438 if (lastplusoff < x && x - lastplusoff > grain)
439 rc = table_add_freespace(cxt, tb, lastplusoff, x, cont);
5139eca7
KZ
440 return rc;
441}
442
2cec7949 443
6c89f750 444/**
2cec7949 445 * fdisk_get_freespaces
6c89f750 446 * @cxt: fdisk context
2cec7949 447 * @tb: returns table
6c89f750 448 *
2cec7949 449 * This function adds freespace (described by fdisk_partition) to @table, it
5139eca7 450 * allocates a new table if the @table points to NULL.
c743bf74 451 *
5175ae87 452 * Note that free space smaller than grain (see fdisk_get_grain()) is ignored.
2cec7949 453
6c89f750
KZ
454 * Returns 0 on success, otherwise, a corresponding error.
455 */
2cec7949 456int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb)
6c89f750 457{
bd5e8291 458 int rc = 0;
6c89f750 459 uint64_t last, grain;
e5c93999 460 struct fdisk_table *parts = NULL;
5139eca7 461 struct fdisk_partition *pa;
e5c93999 462 struct fdisk_iter itr;
6c89f750 463
d71bd2f0 464 DBG(CXT, ul_debugobj(cxt, "get freespace"));
2cec7949 465
6c89f750
KZ
466 if (!cxt || !cxt->label || !tb)
467 return -EINVAL;
2cec7949
KZ
468 if (!*tb && !(*tb = fdisk_new_table()))
469 return -ENOMEM;
5139eca7 470
e5c93999
KZ
471 rc = fdisk_get_partitions(cxt, &parts);
472 if (rc)
473 goto done;
e5c93999
KZ
474 fdisk_table_sort_partitions(parts, fdisk_partition_cmp_start);
475 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
6c89f750 476 last = cxt->first_lba;
03643931 477 grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
6c89f750 478
e5c93999 479 /* analyze gaps between partitions */
5139eca7 480 while (rc == 0 && fdisk_table_next_partition(parts, &itr, &pa) == 0) {
ecf40cda
KZ
481 if (!pa->used || pa->wholedisk || fdisk_partition_is_nested(pa)
482 || !fdisk_partition_has_start(pa))
03643931 483 continue;
d71bd2f0 484 DBG(CXT, ul_debugobj(cxt, "freespace analyze: partno=%zu, start=%ju, end=%ju",
e5c93999 485 pa->partno, pa->start, pa->end));
c743bf74 486 if (last + grain <= pa->start) {
03643931 487 rc = table_add_freespace(cxt, *tb,
6c89f750 488 last + (last > cxt->first_lba ? 1 : 0),
bd5e8291 489 pa->start - 1, NULL);
6c89f750 490 }
5139eca7
KZ
491 /* add gaps between logical partitions */
492 if (fdisk_partition_is_container(pa))
493 rc = check_container_freespace(cxt, parts, *tb, pa);
6c89f750 494 last = pa->end;
6c89f750
KZ
495 }
496
5139eca7
KZ
497 /* add free-space behind last partition to the end of the table (so
498 * don't use table_add_freespace()) */
499 if (rc == 0 && last + grain < cxt->total_sectors - 1) {
500 rc = new_freespace(cxt,
6c89f750 501 last + (last > cxt->first_lba ? 1 : 0),
5139eca7
KZ
502 cxt->last_lba, NULL, &pa);
503 if (pa) {
504 fdisk_table_add_partition(*tb, pa);
505 fdisk_unref_partition(pa);
03643931 506 }
aa423fd3 507 }
03643931 508
e5c93999
KZ
509done:
510 fdisk_unref_table(parts);
2cec7949 511 return rc;
6c89f750
KZ
512}
513
8b60872e
KZ
514/**
515 * fdisk_table_wrong_order:
516 * @tb: table
517 *
518 * Returns: 1 of the table is not in disk order
519 */
520int fdisk_table_wrong_order(struct fdisk_table *tb)
521{
522 struct fdisk_partition *pa;
523 struct fdisk_iter itr;
524 sector_t last = 0;
525
526 DBG(TAB, ul_debugobj(tb, "wrong older check"));
527
528 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
529 while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
ecf40cda
KZ
530 if (!fdisk_partition_has_start(pa))
531 continue;
8b60872e
KZ
532 if (pa->start < last)
533 return 1;
534 last = pa->start;
535 }
536 return 0;
537}
385810d2 538
3c0e6b15
KZ
539/**
540 * fdisk_apply_table:
541 * @cxt: context
542 * @tb: table
543 *
544 * Add partitions from table @tb to the in-memory disk label. See
3186f4a9
KZ
545 * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitons
546 * that does not define start (or does not follow the default start)
547 * are ingored.
3c0e6b15
KZ
548 *
549 * Returns: 0 on success, <0 on error.
550 */
551int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb)
552{
553 struct fdisk_partition *pa;
554 struct fdisk_iter itr;
555 int rc = 0;
556
557 assert(cxt);
558 assert(tb);
559
560 DBG(TAB, ul_debugobj(tb, "applying to context %p", cxt));
561
562 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
563 while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
ecf40cda 564 if (!fdisk_partition_has_start(pa) && !pa->start_follow_default)
3186f4a9 565 continue;
c3bc7483 566 rc = fdisk_add_partition(cxt, pa, NULL);
3c0e6b15
KZ
567 if (rc)
568 break;
569 }
570
571 return rc;
572}
573