Coverage Report

Created: 2025-11-16 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libfdisk/src/table.c
Line
Count
Source
1
2
#include "fdiskP.h"
3
4
/**
5
 * SECTION: table
6
 * @title: Table
7
 * @short_description: container for fdisk partitions
8
 *
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.
12
 */
13
14
/**
15
 * fdisk_new_table:
16
 *
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
19
 * real on-disk data.
20
 *
21
 * Returns: newly allocated table struct.
22
 */
23
struct fdisk_table *fdisk_new_table(void)
24
3.65k
{
25
3.65k
  struct fdisk_table *tb = NULL;
26
27
3.65k
  tb = calloc(1, sizeof(*tb));
28
3.65k
  if (!tb)
29
0
    return NULL;
30
31
3.65k
  DBG(TAB, ul_debugobj(tb, "alloc"));
32
3.65k
  tb->refcount = 1;
33
3.65k
  INIT_LIST_HEAD(&tb->parts);
34
3.65k
  return tb;
35
3.65k
}
36
37
/**
38
 * fdisk_reset_table:
39
 * @tb: tab pointer
40
 *
41
 * Removes all entries (partitions) from the table. The partitions with zero
42
 * reference count will be deallocated. This function does not modify partition
43
 * table.
44
 *
45
 * Returns: 0 on success or negative number in case of error.
46
 */
47
int fdisk_reset_table(struct fdisk_table *tb)
48
7.31k
{
49
7.31k
  if (!tb)
50
0
    return -EINVAL;
51
52
7.31k
  DBG(TAB, ul_debugobj(tb, "reset"));
53
54
25.2k
  while (!list_empty(&tb->parts)) {
55
17.9k
    struct fdisk_partition *pa = list_entry(tb->parts.next,
56
17.9k
                          struct fdisk_partition, parts);
57
17.9k
    fdisk_table_remove_partition(tb, pa);
58
17.9k
  }
59
60
7.31k
  tb->nents = 0;
61
7.31k
  return 0;
62
7.31k
}
63
64
/**
65
 * fdisk_ref_table:
66
 * @tb: table pointer
67
 *
68
 * Increments reference counter.
69
 */
70
void fdisk_ref_table(struct fdisk_table *tb)
71
0
{
72
0
  if (tb)
73
0
    tb->refcount++;
74
0
}
75
76
/**
77
 * fdisk_unref_table:
78
 * @tb: table pointer
79
 *
80
 * Decrements reference counter, on zero the @tb is automatically
81
 * deallocated.
82
 */
83
void fdisk_unref_table(struct fdisk_table *tb)
84
3.68k
{
85
3.68k
  if (!tb)
86
31
    return;
87
88
3.65k
  tb->refcount--;
89
3.65k
  if (tb->refcount <= 0) {
90
3.65k
    fdisk_reset_table(tb);
91
92
3.65k
    DBG(TAB, ul_debugobj(tb, "free"));
93
3.65k
    free(tb);
94
3.65k
  }
95
3.65k
}
96
97
/**
98
 * fdisk_table_is_empty:
99
 * @tb: pointer to tab
100
 *
101
 * Returns: 1 if the table is without filesystems, or 0.
102
 */
103
int fdisk_table_is_empty(struct fdisk_table *tb)
104
27.1k
{
105
27.1k
  return tb == NULL || list_empty(&tb->parts) ? 1 : 0;
106
27.1k
}
107
108
/**
109
 * fdisk_table_get_nents:
110
 * @tb: pointer to tab
111
 *
112
 * Returns: number of entries in table.
113
 */
114
size_t fdisk_table_get_nents(struct fdisk_table *tb)
115
0
{
116
0
  return tb ? tb->nents : 0;
117
0
}
118
119
/**
120
 * fdisk_table_next_partition:
121
 * @tb: tab pointer
122
 * @itr: iterator
123
 * @pa: returns the next tab entry
124
 *
125
 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
126
 *
127
 * Example:
128
 * <informalexample>
129
 *   <programlisting>
130
 *  while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
131
 *    ...
132
 *  }
133
 *   </programlisting>
134
 * </informalexample>
135
 */
136
int fdisk_table_next_partition(
137
      struct fdisk_table *tb,
138
      struct fdisk_iter *itr,
139
      struct fdisk_partition **pa)
140
19.8k
{
141
19.8k
  int rc = 1;
142
143
19.8k
  if (!tb || !itr || !pa)
144
0
    return -EINVAL;
145
19.8k
  *pa = NULL;
146
147
19.8k
  if (!itr->head)
148
1.91k
    FDISK_ITER_INIT(itr, &tb->parts);
149
19.8k
  if (itr->p != itr->head) {
150
17.9k
    FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts);
151
17.9k
    rc = 0;
152
17.9k
  }
153
154
19.8k
  return rc;
155
19.8k
}
156
157
/**
158
 * fdisk_table_get_partition:
159
 * @tb: tab pointer
160
 * @n: number of entry in table
161
 *
162
 * Returns: n-th entry from table or NULL
163
 */
164
struct fdisk_partition *fdisk_table_get_partition(
165
      struct fdisk_table *tb,
166
      size_t n)
167
0
{
168
0
  struct fdisk_partition *pa = NULL;
169
0
  struct fdisk_iter itr;
170
171
0
  if (!tb)
172
0
    return NULL;
173
174
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
175
176
0
  while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
177
0
    if (n == 0)
178
0
      return pa;
179
0
    n--;
180
0
  }
181
182
0
  return NULL;
183
0
}
184
185
/**
186
 * fdisk_table_get_partition_by_partno:
187
 * @tb: tab pointer
188
 * @partno: partition number
189
 *
190
 * Returns: partition with @partno or NULL.
191
 */
192
struct fdisk_partition *fdisk_table_get_partition_by_partno(
193
      struct fdisk_table *tb,
194
      size_t partno)
195
0
{
196
0
  struct fdisk_partition *pa = NULL;
197
0
  struct fdisk_iter itr;
198
199
0
  if (!tb)
200
0
    return NULL;
201
202
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
203
204
0
  while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
205
0
    if (pa->partno == partno)
206
0
      return pa;
207
0
  }
208
209
0
  return NULL;
210
0
}
211
212
/**
213
 * fdisk_table_add_partition
214
 * @tb: tab pointer
215
 * @pa: new entry
216
 *
217
 * Adds a new entry to table and increment @pa reference counter. Don't forget to
218
 * use fdisk_unref_partition() after fdisk_table_add_partition() if you want to keep
219
 * the @pa referenced by the table only.
220
 *
221
 * Returns: 0 on success or negative number in case of error.
222
 */
223
int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
224
17.9k
{
225
17.9k
  if (!tb || !pa)
226
0
    return -EINVAL;
227
228
17.9k
  if (!list_empty(&pa->parts))
229
0
    return -EBUSY;
230
231
17.9k
  fdisk_ref_partition(pa);
232
17.9k
  list_add_tail(&pa->parts, &tb->parts);
233
17.9k
  tb->nents++;
234
235
17.9k
  DBG(TAB, ul_debugobj(tb, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
236
17.9k
      pa,
237
17.9k
      (uintmax_t) fdisk_partition_get_start(pa),
238
17.9k
      fdisk_partition_has_end(pa) ? (uintmax_t) fdisk_partition_get_end(pa) : 0,
239
17.9k
      fdisk_partition_has_size(pa) ? (uintmax_t) fdisk_partition_get_size(pa) : 0,
240
17.9k
      fdisk_partition_is_freespace(pa) ? "freespace" : "",
241
17.9k
      fdisk_partition_is_nested(pa)    ? "nested"    : "",
242
17.9k
      fdisk_partition_is_container(pa) ? "container" : "primary"));
243
17.9k
  return 0;
244
17.9k
}
245
246
/* inserts @pa after @poz */
247
static int table_insert_partition(
248
      struct fdisk_table *tb,
249
      struct fdisk_partition *poz,
250
      struct fdisk_partition *pa)
251
0
{
252
0
  assert(tb);
253
0
  assert(pa);
254
255
0
  fdisk_ref_partition(pa);
256
0
  if (poz)
257
0
    list_add(&pa->parts, &poz->parts);
258
0
  else
259
0
    list_add(&pa->parts, &tb->parts);
260
0
  tb->nents++;
261
262
0
  DBG(TAB, ul_debugobj(tb, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
263
0
      pa, poz ? poz : NULL,
264
0
      (uintmax_t) fdisk_partition_get_start(pa),
265
0
      (uintmax_t) fdisk_partition_get_end(pa),
266
0
      (uintmax_t) fdisk_partition_get_size(pa),
267
0
      fdisk_partition_is_freespace(pa) ? "freespace" : "",
268
0
      fdisk_partition_is_nested(pa)    ? "nested"    : "",
269
0
      fdisk_partition_is_container(pa) ? "container" : ""));
270
0
  return 0;
271
0
}
272
273
/**
274
 * fdisk_table_remove_partition
275
 * @tb: tab pointer
276
 * @pa: new entry
277
 *
278
 * Removes the @pa from the table and de-increment reference counter of the @pa. The
279
 * partition with zero reference counter will be deallocated. Don't forget to use
280
 * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
281
 * to use @pa later.
282
 *
283
 * Returns: 0 on success or negative number in case of error.
284
 */
285
int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
286
17.9k
{
287
17.9k
  if (!tb || !pa)
288
0
    return -EINVAL;
289
290
17.9k
  DBG(TAB, ul_debugobj(tb, "remove entry %p", pa));
291
17.9k
  list_del(&pa->parts);
292
17.9k
  INIT_LIST_HEAD(&pa->parts);
293
294
17.9k
  fdisk_unref_partition(pa);
295
17.9k
  tb->nents--;
296
297
17.9k
  return 0;
298
17.9k
}
299
300
/**
301
 * fdisk_get_partitions
302
 * @cxt: fdisk context
303
 * @tb: returns table
304
 *
305
 * This function adds partitions from disklabel to @table, it allocates a new
306
 * table if @table points to NULL.
307
 *
308
 * Returns: 0 on success, otherwise, a corresponding error.
309
 */
310
int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb)
311
0
{
312
0
  size_t i;
313
314
0
  if (!cxt || !cxt->label || !tb)
315
0
    return -EINVAL;
316
0
  if (!cxt->label->op->get_part)
317
0
    return -ENOSYS;
318
319
0
  DBG(CXT, ul_debugobj(cxt, " -- get table --"));
320
321
0
  if (!*tb && !(*tb = fdisk_new_table()))
322
0
    return -ENOMEM;
323
324
0
  for (i = 0; i < cxt->label->nparts_max; i++) {
325
0
    struct fdisk_partition *pa = NULL;
326
327
0
    if (fdisk_get_partition(cxt, i, &pa) != 0)
328
0
      continue;
329
0
    if (fdisk_partition_is_used(pa))
330
0
      fdisk_table_add_partition(*tb, pa);
331
0
    fdisk_unref_partition(pa);
332
0
  }
333
334
0
  return 0;
335
0
}
336
337
void fdisk_debug_print_table(struct fdisk_table *tb)
338
0
{
339
0
  struct fdisk_iter itr;
340
0
  struct fdisk_partition *pa;
341
342
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
343
0
  while (fdisk_table_next_partition(tb, &itr, &pa) == 0)
344
0
    ul_debugobj(tb, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju%s%s%s] ",
345
0
          pa, pa->partno,
346
0
          (uintmax_t) fdisk_partition_get_start(pa),
347
0
          (uintmax_t) fdisk_partition_get_end(pa),
348
0
          (uintmax_t) fdisk_partition_get_size(pa),
349
0
          fdisk_partition_is_nested(pa) ? " nested" : "",
350
0
          fdisk_partition_is_freespace(pa) ? " freespace" : "",
351
0
          fdisk_partition_is_container(pa) ? " container" : "");
352
353
0
}
354
355
356
typedef int (*fdisk_partcmp_t)(struct fdisk_partition *, struct fdisk_partition *);
357
358
static int cmp_parts_wrapper(struct list_head *a, struct list_head *b, void *data)
359
0
{
360
0
  struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts),
361
0
             *pb = list_entry(b, struct fdisk_partition, parts);
362
363
0
  fdisk_partcmp_t cmp = (fdisk_partcmp_t) data;
364
365
0
  return cmp(pa, pb);
366
0
}
367
368
369
/**
370
 * fdisk_table_sort_partitions:
371
 * @tb: table
372
 * @cmp: compare function
373
 *
374
 * Sort partition in the table.
375
 *
376
 * Returns: 0 on success, <0 on error.
377
 */
378
int fdisk_table_sort_partitions(struct fdisk_table *tb,
379
      int (*cmp)(struct fdisk_partition *,
380
           struct fdisk_partition *))
381
0
{
382
0
  if (!tb)
383
0
    return -EINVAL;
384
385
  /*
386
  DBG(TAB, ul_debugobj(tb, "Before sort:"));
387
  ON_DBG(TAB, fdisk_debug_print_table(tb));
388
  */
389
390
0
  list_sort(&tb->parts, cmp_parts_wrapper, (void *) cmp);
391
392
  /*
393
  DBG(TAB, ul_debugobj(tb, "After sort:"));
394
  ON_DBG(TAB, fdisk_debug_print_table(tb));
395
  */
396
397
0
  return 0;
398
0
}
399
400
/* allocates a new freespace description */
401
static int new_freespace(struct fdisk_context *cxt,
402
       fdisk_sector_t start,
403
       fdisk_sector_t end,
404
       struct fdisk_partition *parent,
405
       struct fdisk_partition **pa)
406
0
{
407
0
  fdisk_sector_t aligned_start, size;
408
409
0
  assert(cxt);
410
0
  assert(pa);
411
412
0
  *pa = NULL;
413
414
0
  if (start == end)
415
0
    return 0;
416
417
0
  assert(start >= cxt->first_lba);
418
0
  assert(end);
419
0
  assert(end > start);
420
421
0
  aligned_start = fdisk_align_lba_in_range(cxt, start, start, end);
422
0
  size = end - aligned_start + 1ULL;
423
424
0
  if (size == 0) {
425
0
    DBG(TAB, ul_debug("ignore freespace (aligned size is zero)"));
426
0
    return 0;
427
0
  }
428
429
0
  *pa = fdisk_new_partition();
430
0
  if (!*pa)
431
0
    return -ENOMEM;
432
433
0
  (*pa)->freespace = 1;
434
0
  (*pa)->start = aligned_start;
435
0
  (*pa)->size = size;
436
437
0
  if (parent)
438
0
    (*pa)->parent_partno = parent->partno;
439
0
  return 0;
440
0
}
441
442
/* add freespace description to the right place within @tb */
443
static int table_add_freespace(
444
      struct fdisk_context *cxt,
445
      struct fdisk_table *tb,
446
      fdisk_sector_t start,
447
      fdisk_sector_t end,
448
      struct fdisk_partition *parent)
449
0
{
450
0
  struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL;
451
0
  struct fdisk_iter itr;
452
0
  int rc = 0;
453
454
0
  assert(tb);
455
456
0
  rc = new_freespace(cxt, start, end, parent, &pa);
457
0
  if (rc)
458
0
    return -ENOMEM;
459
0
  if (!pa)
460
0
    return 0;
461
462
0
  assert(fdisk_partition_has_start(pa));
463
0
  assert(fdisk_partition_has_end(pa));
464
465
0
  DBG(TAB, ul_debugobj(tb, "adding freespace"));
466
467
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
468
0
  if (parent && fdisk_partition_has_partno(parent)) {
469
0
    while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
470
0
      if (!fdisk_partition_has_partno(x))
471
0
        continue;
472
0
      if (x->partno == parent->partno) {
473
0
        real_parent = x;
474
0
        break;
475
0
      }
476
0
    }
477
0
    if (!real_parent) {
478
0
      DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%zu)",
479
0
          parent->partno));
480
0
      fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
481
0
    }
482
0
  }
483
484
0
  while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
485
0
    fdisk_sector_t the_end, best_end = 0;
486
487
0
    if (!fdisk_partition_has_end(x))
488
0
      continue;
489
490
0
    the_end = fdisk_partition_get_end(x);
491
0
    if (best)
492
0
      best_end = fdisk_partition_get_end(best);
493
494
0
    if (the_end < pa->start && (!best || best_end < the_end))
495
0
      best = x;
496
0
  }
497
498
0
  if (!best && real_parent)
499
0
    best = real_parent;
500
0
  rc = table_insert_partition(tb, best, pa);
501
502
0
  fdisk_unref_partition(pa);
503
504
0
  DBG(TAB, ul_debugobj(tb, "adding freespace DONE [rc=%d]", rc));
505
0
  return rc;
506
0
}
507
508
/* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
509
 * that @parts has to be sorted by partition starts */
510
static int check_container_freespace(struct fdisk_context *cxt,
511
             struct fdisk_table *parts,
512
             struct fdisk_table *tb,
513
             struct fdisk_partition *cont)
514
0
{
515
0
  struct fdisk_iter itr;
516
0
  struct fdisk_partition *pa;
517
0
  fdisk_sector_t x, last, grain, lastplusoff;
518
0
  int rc = 0;
519
520
0
  assert(cxt);
521
0
  assert(parts);
522
0
  assert(tb);
523
0
  assert(cont);
524
0
  assert(fdisk_partition_has_start(cont));
525
526
0
  DBG(TAB, ul_debugobj(tb, "analyze container 0x%p", cont));
527
528
0
  last = fdisk_partition_get_start(cont);
529
0
  grain = cxt->grain > cxt->sector_size ?  cxt->grain / cxt->sector_size : 1;
530
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
531
532
0
  DBG(CXT, ul_debugobj(cxt, "initialized:  last=%ju, grain=%ju",
533
0
                       (uintmax_t)last,  (uintmax_t)grain));
534
535
0
  while (fdisk_table_next_partition(parts, &itr, &pa) == 0) {
536
537
0
    DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju",
538
0
                         pa->partno, (uintmax_t)pa->start));
539
540
0
    if (!pa->used || !fdisk_partition_is_nested(pa)
541
0
            || !fdisk_partition_has_start(pa))
542
0
      continue;
543
544
0
    DBG(CXT, ul_debugobj(cxt, "freespace container analyze: partno=%zu, start=%ju, end=%ju",
545
0
          pa->partno,
546
0
          (uintmax_t) fdisk_partition_get_start(pa),
547
0
          (uintmax_t) fdisk_partition_get_end(pa)));
548
549
0
    lastplusoff = last + cxt->first_lba;
550
0
    if (pa->start > lastplusoff && pa->start - lastplusoff > grain)
551
0
      rc = table_add_freespace(cxt, tb, lastplusoff, pa->start, cont);
552
0
    if (rc)
553
0
      goto done;
554
0
    last = fdisk_partition_get_end(pa);
555
0
  }
556
557
  /* free-space remaining in extended partition */
558
0
  x = fdisk_partition_get_start(cont) + fdisk_partition_get_size(cont) - 1;
559
0
  lastplusoff = last + cxt->first_lba;
560
0
  if (lastplusoff < x && x - lastplusoff > grain) {
561
0
    DBG(TAB, ul_debugobj(tb, "add remaining space in container 0x%p", cont));
562
0
    rc = table_add_freespace(cxt, tb, lastplusoff, x, cont);
563
0
  }
564
565
0
done:
566
0
  DBG(TAB, ul_debugobj(tb, "analyze container 0x%p DONE [rc=%d]", cont, rc));
567
0
  return rc;
568
0
}
569
570
571
/**
572
 * fdisk_get_freespaces
573
 * @cxt: fdisk context
574
 * @tb: returns table
575
 *
576
 * This function adds freespace (described by fdisk_partition) to @table, it
577
 * allocates a new table if the @table points to NULL.
578
 *
579
 * Note that free space smaller than grain (see fdisk_get_grain_size()) is
580
 * ignored.
581
 *
582
 * Returns: 0 on success, otherwise, a corresponding error.
583
 */
584
int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb)
585
0
{
586
0
  int rc = 0;
587
0
  size_t nparts = 0;
588
0
  fdisk_sector_t last, grain;
589
0
  struct fdisk_table *parts = NULL;
590
0
  struct fdisk_partition *pa;
591
0
  struct fdisk_iter itr;
592
593
0
  DBG(CXT, ul_debugobj(cxt, "-- get freespace --"));
594
595
0
  if (!cxt || !cxt->label || !tb)
596
0
    return -EINVAL;
597
0
  if (!*tb && !(*tb = fdisk_new_table()))
598
0
    return -ENOMEM;
599
600
0
  rc = fdisk_get_partitions(cxt, &parts);
601
0
  if (rc)
602
0
    goto done;
603
604
0
  fdisk_table_sort_partitions(parts, fdisk_partition_cmp_start);
605
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
606
0
  last = cxt->first_lba;
607
0
  grain = cxt->grain > cxt->sector_size ?  cxt->grain / cxt->sector_size : 1;
608
609
0
  DBG(CXT, ul_debugobj(cxt, "initialized:  last=%ju, grain=%ju",
610
0
                       (uintmax_t)last,  (uintmax_t)grain));
611
612
  /* analyze gaps between partitions */
613
0
  while (rc == 0 && fdisk_table_next_partition(parts, &itr, &pa) == 0) {
614
615
0
    DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju",
616
0
                         pa->partno, (uintmax_t)pa->start));
617
618
0
    if (!pa->used || pa->wholedisk || fdisk_partition_is_nested(pa)
619
0
            || !fdisk_partition_has_start(pa))
620
0
      continue;
621
0
    DBG(CXT, ul_debugobj(cxt, "freespace analyze: partno=%zu, start=%ju, end=%ju",
622
0
          pa->partno,
623
0
          (uintmax_t) fdisk_partition_get_start(pa),
624
0
          (uintmax_t) fdisk_partition_get_end(pa)));
625
626
    /* We ignore small free spaces (smaller than grain) to keep partitions
627
     * aligned, the exception is space before the first partition when
628
     * cxt->first_lba is aligned. */
629
0
    if (last + grain < pa->start
630
0
        || (nparts == 0 &&
631
0
            (fdisk_align_lba(cxt, last, FDISK_ALIGN_UP) <
632
0
       pa->start))) {
633
0
      rc = table_add_freespace(cxt, *tb,
634
0
        last + (nparts == 0 ? 0 : 1),
635
0
        pa->start - 1, NULL);
636
0
    }
637
    /* add gaps between logical partitions */
638
0
    if (fdisk_partition_is_container(pa))
639
0
      rc = check_container_freespace(cxt, parts, *tb, pa);
640
641
0
    if (fdisk_partition_has_end(pa)) {
642
0
      fdisk_sector_t pa_end = fdisk_partition_get_end(pa);
643
0
      if (pa_end > last)
644
0
        last = fdisk_partition_get_end(pa);
645
0
    }
646
0
    nparts++;
647
0
  }
648
649
  /* add free-space behind last partition to the end of the table (so
650
   * don't use table_add_freespace()) */
651
0
  if (rc == 0 && last + grain < cxt->last_lba - 1) {
652
0
    DBG(CXT, ul_debugobj(cxt, "freespace behind last partition detected"));
653
0
    rc = new_freespace(cxt,
654
0
      last + (last > cxt->first_lba || nparts ? 1 : 0),
655
0
      cxt->last_lba, NULL, &pa);
656
0
    if (pa) {
657
0
      fdisk_table_add_partition(*tb, pa);
658
0
      fdisk_unref_partition(pa);
659
0
    }
660
0
  }
661
662
0
done:
663
0
  fdisk_unref_table(parts);
664
665
0
  DBG(CXT, ul_debugobj(cxt, "get freespace DONE [rc=%d]", rc));
666
0
  return rc;
667
0
}
668
669
/**
670
 * fdisk_table_wrong_order:
671
 * @tb: table
672
 *
673
 * Returns: 1 of the table is not in disk order
674
 */
675
int fdisk_table_wrong_order(struct fdisk_table *tb)
676
0
{
677
0
  struct fdisk_partition *pa;
678
0
  struct fdisk_iter itr;
679
0
  fdisk_sector_t last = 0;
680
681
0
  DBG(TAB, ul_debugobj(tb, "wrong older check"));
682
683
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
684
0
  while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
685
0
    if (!fdisk_partition_has_start(pa) || fdisk_partition_is_wholedisk(pa))
686
0
      continue;
687
0
    if (pa->start < last)
688
0
      return 1;
689
0
    last = pa->start;
690
0
  }
691
0
  return 0;
692
0
}
693
694
/**
695
 * fdisk_apply_table:
696
 * @cxt: context
697
 * @tb: table
698
 *
699
 * Add partitions from table @tb to the in-memory disk label. See
700
 * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitions
701
 * that does not define start (or does not follow the default start)
702
 * are ignored.
703
 *
704
 * Returns: 0 on success, <0 on error.
705
 */
706
int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb)
707
0
{
708
0
  struct fdisk_partition *pa;
709
0
  struct fdisk_iter itr;
710
0
  int rc = 0;
711
712
0
  assert(cxt);
713
0
  assert(tb);
714
715
0
  DBG(TAB, ul_debugobj(tb, "applying to context %p", cxt));
716
717
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
718
0
  while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
719
0
    if (!fdisk_partition_has_start(pa) && !pa->start_follow_default)
720
0
      continue;
721
0
    rc = fdisk_add_partition(cxt, pa, NULL);
722
0
    if (rc)
723
0
      break;
724
0
  }
725
726
0
  return rc;
727
0
}
728
729
int fdisk_diff_tables(struct fdisk_table *a, struct fdisk_table *b,
730
          struct fdisk_iter *itr,
731
          struct fdisk_partition **res, int *change)
732
0
{
733
0
  struct fdisk_partition *pa = NULL, *pb;
734
0
  int rc = 1;
735
736
0
  assert(itr);
737
0
  assert(res);
738
0
  assert(change);
739
740
0
  DBG(TAB, ul_debugobj(a, "table diff [new table=%p]", b));
741
742
0
  if (a && (itr->head == NULL || itr->head == &a->parts)) {
743
0
    DBG(TAB, ul_debugobj(a, " scanning old table"));
744
0
    do {
745
0
      rc = fdisk_table_next_partition(a, itr, &pa);
746
0
      if (rc != 0)
747
0
        break;
748
0
    } while (!fdisk_partition_has_partno(pa));
749
0
  }
750
751
0
  if (rc == 1 && b) {
752
0
    DBG(TAB, ul_debugobj(a, " scanning new table"));
753
0
    if (itr->head != &b->parts) {
754
0
      DBG(TAB, ul_debugobj(a, "  initialize to TAB=%p", b));
755
0
      fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
756
0
    }
757
758
0
    while (fdisk_table_next_partition(b, itr, &pb) == 0) {
759
0
      if (!fdisk_partition_has_partno(pb))
760
0
        continue;
761
0
      if (a == NULL ||
762
0
          fdisk_table_get_partition_by_partno(a, pb->partno) == NULL) {
763
0
        DBG(TAB, ul_debugobj(a, " #%zu ADDED", pb->partno));
764
0
        *change = FDISK_DIFF_ADDED;
765
0
        *res = pb;
766
0
        return 0;
767
0
      }
768
0
    }
769
0
  }
770
771
0
  if (rc) {
772
0
    DBG(TAB, ul_debugobj(a, "table diff done [rc=%d]", rc));
773
0
    return rc;  /* error or done */
774
0
  }
775
776
0
  pb = fdisk_table_get_partition_by_partno(b, pa->partno);
777
778
0
  if (!pb) {
779
0
    DBG(TAB, ul_debugobj(a, " #%zu REMOVED", pa->partno));
780
0
    *change = FDISK_DIFF_REMOVED;
781
0
    *res = pa;
782
0
  } else if (pb->start != pa->start) {
783
0
    DBG(TAB, ul_debugobj(a, " #%zu MOVED", pb->partno));
784
0
    *change = FDISK_DIFF_MOVED;
785
0
    *res = pb;
786
0
  } else if (pb->size != pa->size) {
787
0
    DBG(TAB, ul_debugobj(a, " #%zu RESIZED", pb->partno));
788
0
    *change = FDISK_DIFF_RESIZED;
789
0
    *res = pb;
790
0
  } else {
791
    DBG(TAB, ul_debugobj(a, " #%zu UNCHANGED", pb->partno));
792
0
    *change = FDISK_DIFF_UNCHANGED;
793
0
    *res = pa;
794
0
  }
795
0
  return 0;
796
0
}
797