Coverage Report

Created: 2025-11-11 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libfdisk/src/partition.c
Line
Count
Source
1
2
#include "c.h"
3
#include "strutils.h"
4
5
#ifdef HAVE_LIBBLKID
6
# include <blkid.h>
7
#endif
8
9
#include "fdiskP.h"
10
11
/**
12
 * SECTION: partition
13
 * @title: Partition
14
 * @short_description: generic label independent partition abstraction
15
 *
16
 * The fdisk_partition provides label independent abstraction. The partitions
17
 * are not directly connected with partition table (label) data. Any change to
18
 * fdisk_partition does not affects in-memory or on-disk label data.
19
 *
20
 * The fdisk_partition is possible to use as a template for
21
 * fdisk_add_partition() or fdisk_set_partition() operations.
22
 */
23
24
static void init_partition(struct fdisk_partition *pa)
25
39.2k
{
26
39.2k
  FDISK_INIT_UNDEF(pa->size);
27
39.2k
  FDISK_INIT_UNDEF(pa->start);
28
39.2k
  FDISK_INIT_UNDEF(pa->partno);
29
39.2k
  FDISK_INIT_UNDEF(pa->parent_partno);
30
39.2k
  FDISK_INIT_UNDEF(pa->boot);
31
32
39.2k
  INIT_LIST_HEAD(&pa->parts);
33
39.2k
}
34
35
/**
36
 * fdisk_new_partition:
37
 *
38
 * Returns: new instance.
39
 */
40
struct fdisk_partition *fdisk_new_partition(void)
41
19.6k
{
42
19.6k
  struct fdisk_partition *pa = calloc(1, sizeof(*pa));
43
19.6k
  if (!pa)
44
0
    return NULL;
45
46
19.6k
  pa->refcount = 1;
47
19.6k
  init_partition(pa);
48
19.6k
  DBG(PART, ul_debugobj(pa, "alloc"));
49
19.6k
  return pa;
50
19.6k
}
51
52
/**
53
 * fdisk_reset_partition:
54
 * @pa: partition
55
 *
56
 * Resets partition content.
57
 */
58
void fdisk_reset_partition(struct fdisk_partition *pa)
59
19.6k
{
60
19.6k
  int ref;
61
62
19.6k
  if (!pa)
63
0
    return;
64
65
19.6k
  DBG(PART, ul_debugobj(pa, "reset"));
66
19.6k
  ref = pa->refcount;
67
68
19.6k
  fdisk_unref_parttype(pa->type);
69
19.6k
  free(pa->name);
70
19.6k
  free(pa->uuid);
71
19.6k
  free(pa->attrs);
72
19.6k
  free(pa->fstype);
73
19.6k
  free(pa->fsuuid);
74
19.6k
  free(pa->fslabel);
75
19.6k
  free(pa->start_chs);
76
19.6k
  free(pa->end_chs);
77
78
19.6k
  memset(pa, 0, sizeof(*pa));
79
19.6k
  pa->refcount = ref;
80
81
19.6k
  init_partition(pa);
82
19.6k
}
83
84
static struct fdisk_partition *__copy_partition(struct fdisk_partition *o)
85
0
{
86
0
  struct fdisk_partition *n = fdisk_new_partition();
87
0
  int rc;
88
89
0
  if (!n)
90
0
    return NULL;
91
92
0
  memcpy(n, o, sizeof(*n));
93
94
  /* do not copy reference to lists, etc.*/
95
0
  n->refcount = 1;
96
0
  INIT_LIST_HEAD(&n->parts);
97
98
0
  if (n->type)
99
0
    fdisk_ref_parttype(n->type);
100
101
  /* note that strdup_between_structs() deallocates destination pointer,
102
   * so make sure it's NULL as we call memcpy() before ... */
103
0
  n->name = NULL;
104
0
  rc = strdup_between_structs(n, o, name);
105
106
0
  n->uuid = NULL;
107
0
  if (!rc)
108
0
    rc = strdup_between_structs(n, o, uuid);
109
0
  n->attrs = NULL;
110
0
  if (!rc)
111
0
    rc = strdup_between_structs(n, o, attrs);
112
0
  n->fstype = NULL;
113
0
  if (!rc)
114
0
    rc = strdup_between_structs(n, o, fstype);
115
0
  n->fsuuid = NULL;
116
0
  if (!rc)
117
0
    rc = strdup_between_structs(n, o, fsuuid);
118
0
  n->fslabel = NULL;
119
0
  if (!rc)
120
0
    rc = strdup_between_structs(n, o, fslabel);
121
0
  n->start_chs = NULL;
122
0
  if (!rc)
123
0
    rc = strdup_between_structs(n, o, start_chs);
124
0
  n->end_chs = NULL;
125
0
  if (!rc)
126
0
    rc = strdup_between_structs(n, o, end_chs);
127
128
0
  if (rc) {
129
0
    fdisk_unref_partition(n);
130
0
    n = NULL;
131
0
  }
132
0
  return n;
133
0
}
134
135
/**
136
 * fdisk_ref_partition:
137
 * @pa: partition pointer
138
 *
139
 * Increments reference counter.
140
 */
141
void fdisk_ref_partition(struct fdisk_partition *pa)
142
18.2k
{
143
18.2k
  if (pa)
144
18.2k
    pa->refcount++;
145
18.2k
}
146
147
/**
148
 * fdisk_unref_partition:
149
 * @pa: partition pointer
150
 *
151
 * Decrements reference counter, on zero the @pa is automatically
152
 * deallocated.
153
 */
154
void fdisk_unref_partition(struct fdisk_partition *pa)
155
37.9k
{
156
37.9k
  if (!pa)
157
0
    return;
158
159
37.9k
  pa->refcount--;
160
37.9k
  if (pa->refcount <= 0) {
161
19.6k
    list_del(&pa->parts);
162
19.6k
    fdisk_reset_partition(pa);
163
19.6k
    DBG(PART, ul_debugobj(pa, "free"));
164
19.6k
    free(pa);
165
19.6k
  }
166
37.9k
}
167
168
/**
169
 * fdisk_partition_set_start:
170
 * @pa: partition
171
 * @off: offset in sectors, maximal is UINT64_MAX-1
172
 *
173
 * Note that zero is valid offset too. Use fdisk_partition_unset_start() to
174
 * undefine the offset.
175
 *
176
 * Returns: 0 on success, <0 on error.
177
 */
178
int fdisk_partition_set_start(struct fdisk_partition *pa, fdisk_sector_t off)
179
3.44k
{
180
3.44k
  if (!pa)
181
0
    return -EINVAL;
182
3.44k
  if (FDISK_IS_UNDEF(off))
183
194
    return -ERANGE;
184
3.25k
  pa->start = off;
185
3.25k
  pa->fs_probed = 0;
186
3.25k
  return 0;
187
3.44k
}
188
189
/**
190
 * fdisk_partition_unset_start:
191
 * @pa: partition
192
 *
193
 * Sets the size as undefined. See fdisk_partition_has_start().
194
 *
195
 * Returns: 0 on success, <0 on error.
196
 */
197
int fdisk_partition_unset_start(struct fdisk_partition *pa)
198
0
{
199
0
  if (!pa)
200
0
    return -EINVAL;
201
0
  FDISK_INIT_UNDEF(pa->start);
202
0
  pa->fs_probed = 0;
203
0
  return 0;
204
0
}
205
206
/**
207
 * fdisk_partition_get_start:
208
 * @pa: partition
209
 *
210
 * The zero is also valid offset. The function may return random undefined
211
 * value when start offset is undefined (for example after
212
 * fdisk_partition_unset_start()). Always use fdisk_partition_has_start() to be
213
 * sure that you work with valid numbers.
214
 *
215
 * Returns: start offset in sectors
216
 */
217
fdisk_sector_t fdisk_partition_get_start(struct fdisk_partition *pa)
218
0
{
219
0
  return pa->start;
220
0
}
221
222
/**
223
 * fdisk_partition_has_start:
224
 * @pa: partition
225
 *
226
 * Returns: 1 or 0
227
 */
228
int fdisk_partition_has_start(struct fdisk_partition *pa)
229
18.2k
{
230
18.2k
  return pa && !FDISK_IS_UNDEF(pa->start);
231
18.2k
}
232
233
234
/**
235
 * fdisk_partition_cmp_start:
236
 * @a: partition
237
 * @b: partition
238
 *
239
 * Compares partitions according to start offset, See fdisk_table_sort_partitions().
240
 *
241
 * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
242
 */
243
int fdisk_partition_cmp_start(struct fdisk_partition *a,
244
            struct fdisk_partition *b)
245
0
{
246
0
  int no_a = FDISK_IS_UNDEF(a->start),
247
0
      no_b = FDISK_IS_UNDEF(b->start);
248
249
0
  if (no_a && no_b)
250
0
    return 0;
251
0
  if (no_a)
252
0
    return -1;
253
0
  if (no_b)
254
0
    return 1;
255
256
0
  return cmp_numbers(a->start, b->start);
257
0
}
258
259
/**
260
 * fdisk_partition_start_follow_default
261
 * @pa: partition
262
 * @enable: 0|1
263
 *
264
 * When @pa is used as a template for fdisk_add_partition(), it follows the driver's default for
265
 * the beginning of the partition. For DOS, it is the first usable space, while for GPT, it is
266
 * the first sector of the largest area.
267
 *
268
 * Returns: 0 on success, <0 on error.
269
 */
270
int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable)
271
34.3k
{
272
34.3k
  if (!pa)
273
0
    return -EINVAL;
274
34.3k
  pa->start_follow_default = enable ? 1 : 0;
275
34.3k
  return 0;
276
34.3k
}
277
278
/**
279
 * fdisk_partition_start_is_default:
280
 * @pa: partition
281
 *
282
 * See fdisk_partition_start_follow_default().
283
 *
284
 * Returns: 1 if the partition follows default
285
 */
286
int fdisk_partition_start_is_default(struct fdisk_partition *pa)
287
0
{
288
0
  assert(pa);
289
0
  return pa->start_follow_default;
290
0
}
291
292
/**
293
 * fdisk_partition_set_size:
294
 * @pa: partition
295
 * @sz: size in sectors, maximal is UIN64_MAX-1
296
 *
297
 * Note that zero is valid size too. Use fdisk_partition_unset_size() to
298
 * undefine the size.
299
 *
300
 * Returns: 0 on success, <0 on error.
301
 */
302
int fdisk_partition_set_size(struct fdisk_partition *pa, fdisk_sector_t sz)
303
1.55k
{
304
1.55k
  if (!pa)
305
0
    return -EINVAL;
306
1.55k
  if (FDISK_IS_UNDEF(sz))
307
194
    return -ERANGE;
308
1.35k
  pa->size = sz;
309
1.35k
  pa->fs_probed = 0;
310
1.35k
  return 0;
311
1.55k
}
312
313
/**
314
 * fdisk_partition_unset_size:
315
 * @pa: partition
316
 *
317
 * Sets the size as undefined. See fdisk_partition_has_size().
318
 *
319
 * Returns: 0 on success, <0 on error.
320
 */
321
int fdisk_partition_unset_size(struct fdisk_partition *pa)
322
0
{
323
0
  if (!pa)
324
0
    return -EINVAL;
325
0
  FDISK_INIT_UNDEF(pa->size);
326
0
  pa->fs_probed = 0;
327
0
  return 0;
328
0
}
329
330
/**
331
 * fdisk_partition_get_size:
332
 * @pa: partition
333
 *
334
 * The zero is also valid size. The function may return random undefined
335
 * value when size is undefined (for example after fdisk_partition_unset_size()).
336
 * Always use fdisk_partition_has_size() to be sure that you work with valid
337
 * numbers.
338
 *
339
 * Returns: size offset in sectors
340
 */
341
fdisk_sector_t fdisk_partition_get_size(struct fdisk_partition *pa)
342
0
{
343
0
  return pa->size;
344
0
}
345
346
/**
347
 * fdisk_partition_has_size:
348
 * @pa: partition
349
 *
350
 * Returns: 1 or 0
351
 */
352
int fdisk_partition_has_size(struct fdisk_partition *pa)
353
18.2k
{
354
18.2k
  return pa && !FDISK_IS_UNDEF(pa->size);
355
18.2k
}
356
357
/**
358
 * fdisk_partition_size_explicit:
359
 * @pa: partition
360
 * @enable: 0|1
361
 *
362
 * By default libfdisk aligns the size when add the new partition (by
363
 * fdisk_add_partition()). If you want to disable this functionality use
364
 * @enable = 1.
365
 *
366
 * Returns: 0 on success, <0 on error.
367
 */
368
int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable)
369
1.55k
{
370
1.55k
  if (!pa)
371
0
    return -EINVAL;
372
1.55k
  pa->size_explicit = enable ? 1 : 0;
373
1.55k
  return 0;
374
1.55k
}
375
376
/**
377
 * fdisk_partition_set_partno:
378
 * @pa: partition
379
 * @num: partition number (0 is the first partition, maximal is SIZE_MAX-1)
380
 *
381
 * Note that zero is valid partno too. Use fdisk_partition_unset_partno() to
382
 * undefine the partno.
383
 *
384
 * Returns: 0 on success, <0 on error.
385
 */
386
int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num)
387
432
{
388
432
  if (!pa)
389
0
    return -EINVAL;
390
432
  if (FDISK_IS_UNDEF(num))
391
0
    return -ERANGE;
392
432
  pa->partno = num;
393
432
  return 0;
394
432
}
395
396
/**
397
 * fdisk_partition_unset_partno:
398
 * @pa: partition
399
 *
400
 * Sets the partno as undefined. See fdisk_partition_has_partno().
401
 *
402
 * Returns: 0 on success, <0 on error.
403
 */
404
int fdisk_partition_unset_partno(struct fdisk_partition *pa)
405
0
{
406
0
  if (!pa)
407
0
    return -EINVAL;
408
0
  FDISK_INIT_UNDEF(pa->partno);
409
0
  return 0;
410
0
}
411
412
/**
413
 * fdisk_partition_get_partno:
414
 * @pa: partition
415
 *
416
 * The zero is also valid partition number. The function may return random
417
 * value when partno is undefined (for example after fdisk_partition_unset_partno()).
418
 * Always use fdisk_partition_has_partno() to be sure that you work with valid
419
 * numbers.
420
 *
421
 * Returns: partition number (0 is the first partition)
422
 */
423
size_t fdisk_partition_get_partno(struct fdisk_partition *pa)
424
0
{
425
0
  return pa->partno;
426
0
}
427
428
/**
429
 * fdisk_partition_has_partno:
430
 * @pa: partition
431
 *
432
 * Returns: 1 or 0
433
 */
434
int fdisk_partition_has_partno(struct fdisk_partition *pa)
435
0
{
436
0
  return pa && !FDISK_IS_UNDEF(pa->partno);
437
0
}
438
439
440
/**
441
 * fdisk_partition_cmp_partno:
442
 * @a: partition
443
 * @b: partition
444
 *
445
 * Compares partitions according to partition number See fdisk_table_sort_partitions().
446
 *
447
 * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
448
 */
449
int fdisk_partition_cmp_partno(struct fdisk_partition *a,
450
             struct fdisk_partition *b)
451
0
{
452
0
  return a->partno - b->partno;
453
0
}
454
455
/**
456
 * fdisk_partition_partno_follow_default
457
 * @pa: partition
458
 * @enable: 0|1
459
 *
460
 * When @pa used as a template for fdisk_add_partition() when force label driver
461
 * to add a new partition to the default (next) position.
462
 *
463
 * Returns: 0 on success, <0 on error.
464
 */
465
int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable)
466
20.0k
{
467
20.0k
  if (!pa)
468
0
    return -EINVAL;
469
20.0k
  pa->partno_follow_default = enable ? 1 : 0;
470
20.0k
  return 0;
471
20.0k
}
472
473
/**
474
 * fdisk_partition_set_type:
475
 * @pa: partition
476
 * @type: partition type
477
 *
478
 * Sets partition type.
479
 *
480
 * Returns: 0 on success, <0 on error.
481
 */
482
int fdisk_partition_set_type(struct fdisk_partition *pa,
483
           struct fdisk_parttype *type)
484
0
{
485
0
  if (!pa)
486
0
    return -EINVAL;
487
488
0
  fdisk_ref_parttype(type);
489
0
  fdisk_unref_parttype(pa->type);
490
0
  pa->type = type;
491
492
0
  return 0;
493
0
}
494
495
/**
496
 * fdisk_partition_get_type:
497
 * @pa: partition
498
 *
499
 * Returns: pointer to partition type.
500
 */
501
struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa)
502
0
{
503
0
  return pa ? pa->type : NULL;
504
0
}
505
506
/**
507
 * fdisk_partition_set_name:
508
 * @pa: partition
509
 * @name: partition name
510
 *
511
 * Returns: 0 on success, <0 on error.
512
 */
513
int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name)
514
0
{
515
0
  if (!pa)
516
0
    return -EINVAL;
517
0
  return strdup_to_struct_member(pa, name, name);
518
0
}
519
520
/**
521
 * fdisk_partition_get_name:
522
 * @pa: partition
523
 *
524
 * Returns: partition name
525
 */
526
const char *fdisk_partition_get_name(struct fdisk_partition *pa)
527
0
{
528
0
  return pa ? pa->name : NULL;
529
0
}
530
531
/**
532
 * fdisk_partition_set_uuid:
533
 * @pa: partition
534
 * @uuid: UUID of the partition
535
 *
536
 * Returns: 0 on success, <0 on error.
537
 */
538
int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid)
539
0
{
540
0
  if (!pa)
541
0
    return -EINVAL;
542
0
  return strdup_to_struct_member(pa, uuid, uuid);
543
0
}
544
545
/**
546
 * fdisk_partition_has_end:
547
 * @pa: partition
548
 *
549
 * Returns: 1 if the partition has defined last sector
550
 */
551
int fdisk_partition_has_end(struct fdisk_partition *pa)
552
0
{
553
0
  return pa && !FDISK_IS_UNDEF(pa->start) && !FDISK_IS_UNDEF(pa->size);
554
0
}
555
556
/**
557
 * fdisk_partition_get_end:
558
 * @pa: partition
559
 *
560
 * This function may returns absolute non-sense, always check
561
 * fdisk_partition_has_end().
562
 *
563
 * Note that partition end is defined by fdisk_partition_set_start() and
564
 * fdisk_partition_set_size().
565
 *
566
 * Returns: last partition sector LBA.
567
 */
568
fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa)
569
0
{
570
0
  return pa->start + pa->size - (pa->size == 0 ? 0 : 1);
571
0
}
572
573
/**
574
 * fdisk_partition_end_follow_default
575
 * @pa: partition
576
 * @enable: 0|1
577
 *
578
 * When @pa used as a template for fdisk_add_partition() when force label
579
 * driver to use all the possible space for the new partition.
580
 *
581
 * Returns: 0 on success, <0 on error.
582
 */
583
int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable)
584
28.7k
{
585
28.7k
  if (!pa)
586
0
    return -EINVAL;
587
28.7k
  pa->end_follow_default = enable ? 1 : 0;
588
28.7k
  return 0;
589
28.7k
}
590
591
/**
592
 * fdisk_partition_end_is_default:
593
 * @pa: partition
594
 *
595
 * Returns: 1 if the partition follows default
596
 */
597
int fdisk_partition_end_is_default(struct fdisk_partition *pa)
598
0
{
599
0
  assert(pa);
600
0
  return pa->end_follow_default;
601
0
}
602
603
/**
604
 * fdisk_partition_get_uuid:
605
 * @pa: partition
606
 *
607
 * Returns: partition UUID as string
608
 */
609
const char *fdisk_partition_get_uuid(struct fdisk_partition *pa)
610
0
{
611
0
  return pa ? pa->uuid : NULL;
612
0
}
613
614
/**
615
 * fdisk_partition_get_attrs:
616
 * @pa: partition
617
 *
618
 * Returns: partition attributes in string format
619
 */
620
const char *fdisk_partition_get_attrs(struct fdisk_partition *pa)
621
0
{
622
0
  return pa ? pa->attrs : NULL;
623
0
}
624
625
/**
626
 * fdisk_partition_set_attrs:
627
 * @pa: partition
628
 * @attrs: attributes
629
 *
630
 * Sets @attrs to @pa.
631
 *
632
 * Return: 0 on success, <0 on error.
633
 */
634
int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs)
635
0
{
636
0
  if (!pa)
637
0
    return -EINVAL;
638
0
  return strdup_to_struct_member(pa, attrs, attrs);
639
0
}
640
641
/**
642
 * fdisk_partition_is_nested:
643
 * @pa: partition
644
 *
645
 * Returns: 1 if the partition is nested (e.g. MBR logical partition)
646
 */
647
int fdisk_partition_is_nested(struct fdisk_partition *pa)
648
0
{
649
0
  return pa && !FDISK_IS_UNDEF(pa->parent_partno);
650
0
}
651
652
/**
653
 * fdisk_partition_is_container:
654
 * @pa: partition
655
 *
656
 * Returns: 1 if the partition is container (e.g. MBR extended partition)
657
 */
658
int fdisk_partition_is_container(struct fdisk_partition *pa)
659
0
{
660
0
  return pa && pa->container;
661
0
}
662
663
/**
664
 * fdisk_partition_get_parent:
665
 * @pa: partition
666
 * @parent: parent parno
667
 *
668
 * Returns: returns devno of the parent
669
 */
670
int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
671
0
{
672
0
  if (pa && parent)
673
0
    *parent = pa->parent_partno;
674
0
  else
675
0
    return -EINVAL;
676
0
  return 0;
677
0
}
678
679
/**
680
 * fdisk_partition_is_used:
681
 * @pa: partition
682
 *
683
 * Returns: 1 if the partition points to some area
684
 */
685
int fdisk_partition_is_used(struct fdisk_partition *pa)
686
0
{
687
0
  return pa && pa->used;
688
0
}
689
690
/**
691
 * fdisk_partition_is_bootable:
692
 * @pa: partition
693
 *
694
 * Returns: 1 if the partition has enabled boot flag
695
 */
696
int fdisk_partition_is_bootable(struct fdisk_partition *pa)
697
18.2k
{
698
18.2k
  return pa && pa->boot == 1;
699
18.2k
}
700
701
/**
702
 * fdisk_partition_is_freespace:
703
 * @pa: partition
704
 *
705
 * Returns: 1 if @pa points to freespace
706
 */
707
int fdisk_partition_is_freespace(struct fdisk_partition *pa)
708
0
{
709
0
  return pa && pa->freespace;
710
0
}
711
712
/**
713
 * fdisk_partition_is_wholedisk:
714
 * @pa: partition
715
 *
716
 * Returns: 1 if the partition is special whole-disk (e.g. SUN) partition
717
 */
718
int fdisk_partition_is_wholedisk(struct fdisk_partition *pa)
719
0
{
720
0
  return pa && pa->wholedisk;
721
0
}
722
723
/**
724
 * fdisk_partition_next_partno:
725
 * @pa: partition
726
 * @cxt: context
727
 * @n: returns partition number
728
 *
729
 * If @pa specified and partno-follow-default (see fdisk_partition_partno_follow_default())
730
 * enabled then returns next expected partno or -ERANGE on error.
731
 *
732
 * If @pa is NULL, or @pa does not specify any semantic for the next partno
733
 * then use Ask API to ask user for the next partno. In this case returns 1 if
734
 * no free partition available. If fdisk dialogs are disabled then returns -EINVAL.
735
 *
736
 * Returns: 0 on success, <0 on error, or 1 for non-free partno by Ask API.
737
 */
738
int fdisk_partition_next_partno(
739
    struct fdisk_partition *pa,
740
    struct fdisk_context *cxt,
741
    size_t *n)
742
0
{
743
0
  if (!cxt || !n)
744
0
    return -EINVAL;
745
746
0
  if (pa && pa->partno_follow_default) {
747
0
    size_t i;
748
749
0
    DBG(PART, ul_debugobj(pa, "next partno (follow default)"));
750
751
0
    for (i = 0; i < cxt->label->nparts_max; i++) {
752
0
      if (!fdisk_is_partition_used(cxt, i)) {
753
0
        *n = i;
754
0
        return 0;
755
0
      }
756
0
    }
757
0
    return -ERANGE;
758
759
0
  }
760
761
0
  if (pa && fdisk_partition_has_partno(pa)) {
762
763
0
    DBG(PART, ul_debugobj(pa, "next partno (specified=%zu)", pa->partno));
764
765
0
    if (pa->partno >= cxt->label->nparts_max ||
766
0
        fdisk_is_partition_used(cxt, pa->partno))
767
0
      return -ERANGE;
768
0
    *n = pa->partno;
769
0
    return 0;
770
771
0
  }
772
773
0
  if (fdisk_has_dialogs(cxt))
774
0
    return fdisk_ask_partnum(cxt, n, 1);
775
776
0
  return -EINVAL;
777
0
}
778
779
static int probe_partition_content(struct fdisk_context *cxt, struct fdisk_partition *pa)
780
0
{
781
0
  int rc = 1; /* nothing */
782
783
0
  DBG(PART, ul_debugobj(pa, "start probe #%zu partition [cxt %p] >>>", pa->partno, cxt));
784
785
  /* zeroize the current setting */
786
0
  strdup_to_struct_member(pa, fstype, NULL);
787
0
  strdup_to_struct_member(pa, fsuuid, NULL);
788
0
  strdup_to_struct_member(pa, fslabel, NULL);
789
790
0
  if (!fdisk_partition_has_start(pa) ||
791
0
      !fdisk_partition_has_size(pa))
792
0
    goto done;
793
794
0
#ifdef HAVE_LIBBLKID
795
0
  else {
796
0
    uintmax_t start, size;
797
798
0
    blkid_probe pr = blkid_new_probe();
799
0
    if (!pr)
800
0
      goto done;
801
802
0
    DBG(PART, ul_debugobj(pa, "blkid prober: %p", pr));
803
804
0
    blkid_probe_enable_superblocks(pr, 1);
805
0
    blkid_probe_set_superblocks_flags(pr,
806
0
        BLKID_SUBLKS_MAGIC |
807
0
        BLKID_SUBLKS_TYPE |
808
0
        BLKID_SUBLKS_LABEL |
809
0
        BLKID_SUBLKS_UUID |
810
0
        BLKID_SUBLKS_BADCSUM);
811
812
0
    start = fdisk_partition_get_start(pa) * fdisk_get_sector_size(cxt);
813
0
    size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cxt);
814
815
0
    if (blkid_probe_set_device(pr, cxt->dev_fd, start, size) == 0
816
0
        && blkid_do_fullprobe(pr) == 0) {
817
818
0
      const char *data;
819
0
      rc = 0;
820
821
0
      if (!blkid_probe_lookup_value(pr, "TYPE",  &data, NULL))
822
0
        rc = strdup_to_struct_member(pa, fstype, data);
823
824
0
      if (!rc && !blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
825
0
        rc = strdup_to_struct_member(pa, fslabel, data);
826
827
0
      if (!rc && !blkid_probe_lookup_value(pr, "UUID",  &data, NULL))
828
0
        rc = strdup_to_struct_member(pa, fsuuid, data);
829
0
    }
830
831
0
    blkid_free_probe(pr);
832
0
    pa->fs_probed = 1;
833
0
  }
834
0
#endif /* HAVE_LIBBLKID */
835
836
0
done:
837
0
  DBG(PART, ul_debugobj(pa, "<<< end probe #%zu partition[cxt %p, rc=%d]", pa->partno, cxt, rc));
838
0
  return rc;
839
0
}
840
841
/**
842
 * fdisk_partition_to_string:
843
 * @pa: partition
844
 * @cxt: context
845
 * @id: field (FDISK_FIELD_*)
846
 * @data: returns string with allocated data
847
 *
848
 * Returns info about partition converted to printable string.
849
 *
850
 * For example
851
 * <informalexample>
852
 *   <programlisting>
853
 *      struct fdisk_partition *pa;
854
 *
855
 *      fdisk_get_partition(cxt, 0, &pa);
856
 *  fdisk_partition_to_string(pa, FDISK_FIELD_UUID, &data);
857
 *  printf("first partition uuid: %s\n", data);
858
 *  free(data);
859
 *  fdisk_unref_partition(pa);
860
 *   </programlisting>
861
 * </informalexample>
862
 *
863
 * returns UUID for the first partition.
864
 *
865
 * Returns: 0 on success, otherwise, a corresponding error.
866
 */
867
int fdisk_partition_to_string(struct fdisk_partition *pa,
868
            struct fdisk_context *cxt,
869
            int id,
870
            char **data)
871
0
{
872
0
  char *p = NULL;
873
0
  int rc = 0;
874
0
  uint64_t x;
875
876
0
  if (!pa || !cxt || !data)
877
0
    return -EINVAL;
878
879
0
  switch (id) {
880
0
  case FDISK_FIELD_DEVICE:
881
0
    if (pa->freespace)
882
0
      p = strdup(_("Free space"));
883
0
    else if (fdisk_partition_has_partno(pa) && cxt->dev_path) {
884
0
      if (cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
885
0
        rc = asprintf(&p, "%c", (int) pa->partno + 'a');
886
0
      else
887
0
        p = fdisk_partname(cxt->dev_path, pa->partno + 1);
888
0
    }
889
0
    break;
890
0
  case FDISK_FIELD_BOOT:
891
0
    p = fdisk_partition_is_bootable(pa) ? strdup("*") : NULL;
892
0
    break;
893
0
  case FDISK_FIELD_START:
894
0
    if (fdisk_partition_has_start(pa)) {
895
0
      x = fdisk_cround(cxt, pa->start);
896
0
      rc = pa->start_post ?
897
0
        asprintf(&p, "%"PRIu64"%c", x, pa->start_post) :
898
0
        asprintf(&p, "%"PRIu64, x);
899
0
    }
900
0
    break;
901
0
  case FDISK_FIELD_END:
902
0
    if (fdisk_partition_has_end(pa)) {
903
0
      x = fdisk_cround(cxt, fdisk_partition_get_end(pa));
904
0
      rc = pa->end_post ?
905
0
          asprintf(&p, "%"PRIu64"%c", x, pa->end_post) :
906
0
          asprintf(&p, "%"PRIu64, x);
907
0
    }
908
0
    break;
909
0
  case FDISK_FIELD_SIZE:
910
0
    if (fdisk_partition_has_size(pa)) {
911
0
      uint64_t sz = pa->size * cxt->sector_size;
912
913
0
      switch (cxt->sizeunit) {
914
0
      case FDISK_SIZEUNIT_BYTES:
915
0
        rc = asprintf(&p, "%"PRIu64"", sz);
916
0
        break;
917
0
      case FDISK_SIZEUNIT_HUMAN:
918
0
        if (fdisk_is_details(cxt))
919
0
          rc = pa->size_post ?
920
0
              asprintf(&p, "%"PRIu64"%c", sz, pa->size_post) :
921
0
              asprintf(&p, "%"PRIu64, sz);
922
0
        else {
923
0
          p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
924
0
          if (!p)
925
0
            rc = -ENOMEM;
926
0
        }
927
0
        break;
928
0
      }
929
0
    }
930
0
    break;
931
0
  case FDISK_FIELD_CYLINDERS:
932
0
  {
933
0
    uintmax_t sz = fdisk_partition_has_size(pa) ? pa->size : 0;
934
0
    if (sz)
935
      /* Why we need to cast that to uintmax_t? */
936
0
      rc = asprintf(&p, "%ju", (uintmax_t)(sz / (cxt->geom.heads * cxt->geom.sectors)) + 1);
937
0
    break;
938
0
  }
939
0
  case FDISK_FIELD_SECTORS:
940
0
    rc = asprintf(&p, "%ju",
941
0
      fdisk_partition_has_size(pa) ? (uintmax_t) pa->size : 0);
942
0
    break;
943
0
  case FDISK_FIELD_BSIZE:
944
0
    rc = asprintf(&p, "%"PRIu64, pa->bsize);
945
0
    break;
946
0
  case FDISK_FIELD_FSIZE:
947
0
    rc = asprintf(&p, "%"PRIu64, pa->fsize);
948
0
    break;
949
0
  case FDISK_FIELD_CPG:
950
0
    rc = asprintf(&p, "%"PRIu64, pa->cpg);
951
0
    break;
952
0
  case FDISK_FIELD_TYPE:
953
0
    p = pa->type && pa->type->name ? strdup(_(pa->type->name)) : NULL;
954
0
    break;
955
0
  case FDISK_FIELD_TYPEID:
956
0
    if (pa->type && fdisk_parttype_get_string(pa->type))
957
0
      rc = asprintf(&p, "%s", fdisk_parttype_get_string(pa->type));
958
0
    else if (pa->type)
959
0
      rc = asprintf(&p, "%x", fdisk_parttype_get_code(pa->type));
960
0
    break;
961
0
  case FDISK_FIELD_UUID:
962
0
    p = pa->uuid && *pa->uuid? strdup(pa->uuid) : NULL;
963
0
    break;
964
0
  case FDISK_FIELD_NAME:
965
0
    p = pa->name && *pa->name ? strdup(pa->name) : NULL;
966
0
    break;
967
0
  case FDISK_FIELD_ATTR:
968
0
    p = pa->attrs && *pa->attrs ? strdup(pa->attrs) : NULL;
969
0
    break;
970
0
  case FDISK_FIELD_SADDR:
971
0
    p = pa->start_chs && *pa->start_chs ? strdup(pa->start_chs) : NULL;
972
0
    break;
973
0
  case FDISK_FIELD_EADDR:
974
0
    p = pa->end_chs && *pa->end_chs? strdup(pa->end_chs) : NULL;
975
0
    break;
976
0
  case FDISK_FIELD_FSUUID:
977
0
    if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
978
0
      p = pa->fsuuid && *pa->fsuuid ? strdup(pa->fsuuid) : NULL;
979
0
    break;
980
0
  case FDISK_FIELD_FSLABEL:
981
0
    if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
982
0
      p = pa->fslabel && *pa->fslabel ? strdup(pa->fslabel) : NULL;
983
0
    break;
984
0
  case FDISK_FIELD_FSTYPE:
985
0
    if (pa->fs_probed || probe_partition_content(cxt, pa) == 0)
986
0
      p = pa->fstype && *pa->fstype ? strdup(pa->fstype) : NULL;
987
0
    break;
988
0
  default:
989
0
    return -EINVAL;
990
0
  }
991
992
0
  if (rc < 0) {
993
0
    rc = -ENOMEM;
994
0
    free(p);
995
0
    p = NULL;
996
997
0
  } else if (rc > 0)
998
0
    rc = 0;
999
1000
0
  *data = p;
1001
1002
0
  return rc;
1003
0
}
1004
1005
1006
/**
1007
 * fdisk_get_partition:
1008
 * @cxt: context
1009
 * @partno: partition number (0 is the first partition)
1010
 * @pa: returns data about partition
1011
 *
1012
 * Reads disklabel and fills in @pa with data about partition @n.
1013
 *
1014
 * Note that partno may address unused partition and then this function does
1015
 * not fill anything to @pa.  See fdisk_is_partition_used(). If @pa points to
1016
 * NULL then the function allocates a newly allocated fdisk_partition struct,
1017
 * use fdisk_unref_partition() to deallocate.
1018
 *
1019
 * Returns: 0 on success, otherwise, a corresponding error.
1020
 */
1021
int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
1022
      struct fdisk_partition **pa)
1023
0
{
1024
0
  int rc;
1025
0
  struct fdisk_partition *np = NULL;
1026
1027
0
  if (!cxt || !cxt->label || !pa)
1028
0
    return -EINVAL;
1029
0
  if (!cxt->label->op->get_part)
1030
0
    return -ENOSYS;
1031
0
  if (!fdisk_is_partition_used(cxt, partno))
1032
0
    return -EINVAL;
1033
1034
0
  if (!*pa) {
1035
0
    np = *pa = fdisk_new_partition();
1036
0
    if (!*pa)
1037
0
      return -ENOMEM;
1038
0
  } else
1039
0
    fdisk_reset_partition(*pa);
1040
1041
0
  (*pa)->partno = partno;
1042
0
  rc = cxt->label->op->get_part(cxt, partno, *pa);
1043
1044
0
  if (rc) {
1045
0
    if (np) {
1046
0
      fdisk_unref_partition(np);
1047
0
      *pa = NULL;
1048
0
    } else
1049
0
      fdisk_reset_partition(*pa);
1050
0
  } else
1051
0
    (*pa)->size_explicit = 1;
1052
0
  return rc;
1053
0
}
1054
1055
static struct fdisk_partition *area_by_offset(
1056
      struct fdisk_table *tb,
1057
      struct fdisk_partition *cur,
1058
      fdisk_sector_t off)
1059
0
{
1060
0
  struct fdisk_partition *pa = NULL;
1061
0
  struct fdisk_iter itr;
1062
1063
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
1064
1065
0
  while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
1066
0
    if (!fdisk_partition_has_start(pa) || !fdisk_partition_has_size(pa))
1067
0
      continue;
1068
0
    if (fdisk_partition_is_nested(cur) &&
1069
0
        pa->parent_partno != cur->parent_partno)
1070
0
      continue;
1071
0
    if (off >= pa->start && off < pa->start + pa->size)
1072
0
      return pa;
1073
0
  }
1074
1075
0
  return NULL;
1076
0
}
1077
1078
static int resize_get_first_possible(
1079
      struct fdisk_table *tb,
1080
      struct fdisk_partition *cur,
1081
      fdisk_sector_t *start)
1082
0
{
1083
0
  struct fdisk_partition *pa = NULL, *first = NULL;
1084
0
  struct fdisk_iter itr;
1085
1086
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
1087
1088
0
  *start = 0;
1089
0
  DBG(TAB, ul_debugobj(tb, "checking first possible before start=%ju", (uintmax_t) cur->start));
1090
1091
1092
0
  while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
1093
1094
0
    if (pa->start > cur->start || pa == cur)
1095
0
      break;
1096
1097
0
    DBG(TAB, ul_debugobj(tb, " checking entry %p [partno=%zu start=%ju, end=%ju, size=%ju%s%s%s]",
1098
0
      pa,
1099
0
      fdisk_partition_get_partno(pa),
1100
0
      (uintmax_t) fdisk_partition_get_start(pa),
1101
0
      (uintmax_t) fdisk_partition_get_end(pa),
1102
0
      (uintmax_t) fdisk_partition_get_size(pa),
1103
0
      fdisk_partition_is_freespace(pa) ? " freespace" : "",
1104
0
      fdisk_partition_is_nested(pa)    ? " nested"    : "",
1105
0
      fdisk_partition_is_container(pa) ? " container" : ""));
1106
1107
1108
0
    if (!fdisk_partition_is_freespace(pa)) {
1109
0
      DBG(TAB, ul_debugobj(tb, "  ignored (no freespace)"));
1110
0
      first = NULL;
1111
0
      continue;
1112
0
    }
1113
0
    if (!fdisk_partition_has_start(pa) || !fdisk_partition_has_size(pa)) {
1114
0
      DBG(TAB, ul_debugobj(tb, "  ignored (no start/size)"));
1115
0
      first = NULL;
1116
0
      continue;
1117
0
    }
1118
    /* The current is nested, free space has to be nested within the same parent */
1119
0
    if (fdisk_partition_is_nested(cur)
1120
0
        && pa->parent_partno != cur->parent_partno) {
1121
0
      DBG(TAB, ul_debugobj(tb, "  ignore (nested required)"));
1122
0
      first = NULL;
1123
0
      continue;
1124
0
    }
1125
0
    if (pa->start + pa->size <= cur->start) {
1126
0
      first = pa;
1127
0
      DBG(TAB, ul_debugobj(tb, "  entry usable"));
1128
0
    }
1129
0
  }
1130
1131
0
  if (first)
1132
0
    *start = first->start;
1133
0
  else
1134
0
    DBG(PART, ul_debugobj(cur, "resize: nothing usable before %ju", (uintmax_t) cur->start));
1135
1136
0
  return first ? 0 : -1;
1137
0
}
1138
1139
/*
1140
 * Verify that area addressed by @start is freespace or the @cur[rent]
1141
 * partition and continue to the next table entries until it's freespace, and
1142
 * counts size of all this space.
1143
 *
1144
 * This is core of the partition start offset move operation. We can move the
1145
 * start within the current partition to another free space. It's
1146
 * forbidden to move start of the partition to another already defined
1147
 * partition.
1148
 */
1149
static int resize_get_last_possible(
1150
      struct fdisk_table *tb,
1151
      struct fdisk_partition *cur,
1152
      fdisk_sector_t start,
1153
      fdisk_sector_t *maxsz)
1154
0
{
1155
0
  struct fdisk_partition *pa = NULL, *last = NULL;
1156
0
  struct fdisk_iter itr;
1157
1158
0
  fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
1159
1160
0
  *maxsz = 0;
1161
0
  DBG(TAB, ul_debugobj(tb, "checking last possible for start=%ju", (uintmax_t) start));
1162
1163
1164
0
  while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
1165
1166
0
    DBG(TAB, ul_debugobj(tb, " checking entry %p [partno=%zu start=%ju, end=%ju, size=%ju%s%s%s]",
1167
0
      pa,
1168
0
      fdisk_partition_get_partno(pa),
1169
0
      (uintmax_t) fdisk_partition_get_start(pa),
1170
0
      (uintmax_t) fdisk_partition_get_end(pa),
1171
0
      (uintmax_t) fdisk_partition_get_size(pa),
1172
0
      fdisk_partition_is_freespace(pa) ? " freespace" : "",
1173
0
      fdisk_partition_is_nested(pa)    ? " nested"    : "",
1174
0
      fdisk_partition_is_container(pa) ? " container" : ""));
1175
1176
0
    if (!fdisk_partition_has_start(pa) ||
1177
0
        !fdisk_partition_has_size(pa) ||
1178
0
        (fdisk_partition_is_container(pa) && pa != cur)) {
1179
0
      DBG(TAB, ul_debugobj(tb, "  ignored (no start/size or container)"));
1180
0
      continue;
1181
0
    }
1182
1183
0
    if (fdisk_partition_is_nested(pa)
1184
0
        && fdisk_partition_is_container(cur)
1185
0
        && pa->parent_partno == cur->partno) {
1186
0
      DBG(TAB, ul_debugobj(tb, "  ignore (nested child of the current partition)"));
1187
0
      continue;
1188
0
    }
1189
1190
    /* The current is nested, free space has to be nested within the same parent */
1191
0
    if (fdisk_partition_is_nested(cur)
1192
0
        && pa->parent_partno != cur->parent_partno) {
1193
0
      DBG(TAB, ul_debugobj(tb, "  ignore (nested required)"));
1194
0
      continue;
1195
0
    }
1196
1197
0
    if (!last) {
1198
0
      if (start >= pa->start &&  start < pa->start + pa->size) {
1199
0
        if (fdisk_partition_is_freespace(pa) || pa == cur) {
1200
0
          DBG(TAB, ul_debugobj(tb, "  accepted as last"));
1201
0
          last = pa;
1202
0
        } else {
1203
0
          DBG(TAB, ul_debugobj(tb, "  failed to set last"));
1204
0
          break;
1205
0
        }
1206
1207
1208
0
        *maxsz = pa->size - (start - pa->start);
1209
0
        DBG(TAB, ul_debugobj(tb, "  new max=%ju", (uintmax_t) *maxsz));
1210
0
      }
1211
0
    } else if (!fdisk_partition_is_freespace(pa) && pa != cur) {
1212
0
      DBG(TAB, ul_debugobj(tb, "  no free space behind current"));
1213
0
      break;
1214
0
    } else {
1215
0
      last = pa;
1216
0
      *maxsz = pa->size - (start - pa->start);
1217
0
      DBG(TAB, ul_debugobj(tb, "  new max=%ju (last updated)", (uintmax_t) *maxsz));
1218
0
    }
1219
0
  }
1220
1221
0
  if (last)
1222
0
    DBG(PART, ul_debugobj(cur, "resize: max size=%ju", (uintmax_t) *maxsz));
1223
0
  else
1224
0
    DBG(PART, ul_debugobj(cur, "resize: nothing usable after %ju", (uintmax_t) start));
1225
1226
0
  return last ? 0 : -1;
1227
0
}
1228
1229
/*
1230
 * Uses template @tpl to recount start and size change of the partition @res. The
1231
 * @tpl->size and @tpl->start are interpreted as relative to the current setting.
1232
 */
1233
static int recount_resize(
1234
      struct fdisk_context *cxt, size_t partno,
1235
      struct fdisk_partition *res, struct fdisk_partition *tpl)
1236
0
{
1237
0
  fdisk_sector_t start, size, xsize;
1238
0
  struct fdisk_partition *cur = NULL;
1239
0
  struct fdisk_table *tb = NULL;
1240
0
  int rc;
1241
1242
0
  DBG(PART, ul_debugobj(tpl, ">>> resize requested"));
1243
1244
0
  FDISK_INIT_UNDEF(start);
1245
0
  FDISK_INIT_UNDEF(size);
1246
1247
0
  rc = fdisk_get_partitions(cxt, &tb);
1248
0
  if (!rc) {
1249
    /* For resize we do not follow grain to detect free-space, but
1250
     * we support to resize with very small granulation. */
1251
0
    unsigned long org = cxt->grain;
1252
1253
0
    cxt->grain = cxt->sector_size;
1254
0
    rc = fdisk_get_freespaces(cxt, &tb);
1255
0
    cxt->grain = org;
1256
0
  }
1257
0
  if (rc) {
1258
0
    fdisk_unref_table(tb);
1259
0
    return rc;
1260
0
  }
1261
1262
0
  fdisk_table_sort_partitions(tb, fdisk_partition_cmp_start);
1263
1264
0
  DBG(PART, ul_debugobj(tpl, "resize partition partno=%zu in table:", partno));
1265
0
  ON_DBG(PART, fdisk_debug_print_table(tb));
1266
1267
0
  cur = fdisk_table_get_partition_by_partno(tb, partno);
1268
0
  if (!cur) {
1269
0
    fdisk_unref_table(tb);
1270
0
    return -EINVAL;
1271
0
  }
1272
1273
  /* 1a) set new start - change relative to the current on-disk setting */
1274
0
  if (tpl->movestart && fdisk_partition_has_start(tpl)) {
1275
0
    start = fdisk_partition_get_start(cur);
1276
0
    if (tpl->movestart == FDISK_MOVE_DOWN) {
1277
0
      if (fdisk_partition_get_start(tpl) > start)
1278
0
        goto erange;
1279
0
      start -= fdisk_partition_get_start(tpl);
1280
0
    } else
1281
0
      start += fdisk_partition_get_start(tpl);
1282
1283
0
    DBG(PART, ul_debugobj(tpl, "resize: moving start %s relative, new start: %ju",
1284
0
        tpl->movestart == FDISK_MOVE_DOWN  ? "DOWN" : "UP", (uintmax_t)start));
1285
1286
  /* 1b) set new start - try freespace before the current partition */
1287
0
  } else if (tpl->movestart == FDISK_MOVE_DOWN) {
1288
1289
0
    if (resize_get_first_possible(tb, cur, &start) != 0)
1290
0
      goto erange;
1291
1292
0
    DBG(PART, ul_debugobj(tpl, "resize: moving start DOWN (first possible), new start: %ju",
1293
0
        (uintmax_t)start));
1294
1295
  /* 1c) set new start - absolute number */
1296
0
  } else if (fdisk_partition_has_start(tpl)) {
1297
0
    start = fdisk_partition_get_start(tpl);
1298
0
    DBG(PART, ul_debugobj(tpl, "resize: moving start to absolute offset: %ju",
1299
0
                          (uintmax_t)start));
1300
0
  }
1301
1302
  /* 2) verify that start is within the current partition or any freespace area */
1303
0
  if (!FDISK_IS_UNDEF(start)) {
1304
0
    struct fdisk_partition *area = area_by_offset(tb, cur, start);
1305
1306
0
    if (area == cur)
1307
0
      DBG(PART, ul_debugobj(tpl, "resize: start points to the current partition"));
1308
0
    else if (area && fdisk_partition_is_freespace(area))
1309
0
      DBG(PART, ul_debugobj(tpl, "resize: start points to freespace"));
1310
0
    else if (!area && start >= cxt->first_lba && start < cxt->first_lba + (cxt->grain / cxt->sector_size))
1311
0
      DBG(PART, ul_debugobj(tpl, "resize: start points before first partition"));
1312
0
    else {
1313
0
      DBG(PART, ul_debugobj(tpl, "resize: start verification failed"));
1314
0
      goto erange;
1315
0
    }
1316
0
  } else {
1317
    /* no change, start points to the current partition */
1318
0
    DBG(PART, ul_debugobj(tpl, "resize: start unchanged"));
1319
0
    start = fdisk_partition_get_start(cur);
1320
0
  }
1321
1322
  /* 3a) set new size -- reduce */
1323
0
  if (tpl->resize == FDISK_RESIZE_REDUCE && fdisk_partition_has_size(tpl)) {
1324
0
    DBG(PART, ul_debugobj(tpl, "resize: reduce"));
1325
0
    size = fdisk_partition_get_size(cur);
1326
0
    if (fdisk_partition_get_size(tpl) > size)
1327
0
      goto erange;
1328
0
    size -= fdisk_partition_get_size(tpl);
1329
1330
  /* 3b) set new size -- enlarge */
1331
0
  } else if (tpl->resize == FDISK_RESIZE_ENLARGE && fdisk_partition_has_size(tpl)) {
1332
0
    DBG(PART, ul_debugobj(tpl, "resize: enlarge"));
1333
0
    size = fdisk_partition_get_size(cur);
1334
0
    size += fdisk_partition_get_size(tpl);
1335
1336
  /* 3c) set new size -- no size specified, enlarge to all freespace */
1337
0
  } else if (tpl->resize == FDISK_RESIZE_ENLARGE) {
1338
0
    DBG(PART, ul_debugobj(tpl, "resize: enlarge to all possible"));
1339
0
    if (resize_get_last_possible(tb, cur, start, &size))
1340
0
      goto erange;
1341
1342
  /* 3d) set new size -- absolute number */
1343
0
  } else if (fdisk_partition_has_size(tpl)) {
1344
0
    DBG(PART, ul_debugobj(tpl, "resize: new absolute size"));
1345
0
    size = fdisk_partition_get_size(tpl);
1346
0
  }
1347
1348
  /* 4) verify that size is within the current partition or next free space */
1349
0
  xsize = !FDISK_IS_UNDEF(size) ? size : fdisk_partition_get_size(cur);
1350
1351
0
  if (fdisk_partition_has_size(cur)) {
1352
0
    fdisk_sector_t maxsz;
1353
1354
0
    if (resize_get_last_possible(tb, cur, start, &maxsz))
1355
0
      goto erange;
1356
0
    DBG(PART, ul_debugobj(tpl, "resize: size=%ju, max=%ju",
1357
0
          (uintmax_t) xsize, (uintmax_t) maxsz));
1358
0
    if (xsize > maxsz)
1359
0
      goto erange;
1360
0
  }
1361
1362
0
  if (FDISK_IS_UNDEF(size)) {
1363
0
    DBG(PART, ul_debugobj(tpl, "resize: size unchanged (undefined)"));
1364
0
  }
1365
1366
1367
0
  DBG(PART, ul_debugobj(tpl, "<<< resize: SUCCESS: start %ju->%ju; size %ju->%ju",
1368
0
      (uintmax_t) fdisk_partition_get_start(cur), (uintmax_t) start,
1369
0
      (uintmax_t) fdisk_partition_get_size(cur), (uintmax_t) size));
1370
0
  res->start = start;
1371
0
  res->size = size;
1372
0
  fdisk_unref_table(tb);
1373
0
  return 0;
1374
0
erange:
1375
0
  DBG(PART, ul_debugobj(tpl, "<<< resize: FAILED"));
1376
0
  fdisk_warnx(cxt, _("Failed to resize partition #%zu."), partno + 1);
1377
0
  fdisk_unref_table(tb);
1378
0
  return -ERANGE;
1379
1380
0
}
1381
1382
/**
1383
 * fdisk_set_partition:
1384
 * @cxt: context
1385
 * @partno: partition number (0 is the first partition)
1386
 * @pa: new partition setting
1387
 *
1388
 * Modifies disklabel according to setting with in @pa.
1389
 *
1390
 * Returns: 0 on success, <0 on error.
1391
 */
1392
int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
1393
      struct fdisk_partition *pa)
1394
0
{
1395
0
  struct fdisk_partition *xpa = pa, *tmp = NULL;
1396
0
  int rc, wipe = 0;
1397
1398
0
  if (!cxt || !cxt->label || !pa)
1399
0
    return -EINVAL;
1400
0
  if (!cxt->label->op->set_part)
1401
0
    return -ENOSYS;
1402
1403
0
  pa->fs_probed = 0;
1404
1405
0
  if (!fdisk_is_partition_used(cxt, partno)) {
1406
0
    pa->partno = partno;
1407
0
    return fdisk_add_partition(cxt, pa, NULL);
1408
0
  }
1409
1410
0
  if (pa->resize || pa->movestart
1411
0
      || fdisk_partition_has_start(pa) || fdisk_partition_has_size(pa)) {
1412
0
    xpa = __copy_partition(pa);
1413
0
    if (!xpa) {
1414
0
      rc = -ENOMEM;
1415
0
      goto done;
1416
0
    }
1417
0
    xpa->movestart = 0;
1418
0
    xpa->resize = 0;
1419
0
    FDISK_INIT_UNDEF(xpa->size);
1420
0
    FDISK_INIT_UNDEF(xpa->start);
1421
1422
0
    rc = recount_resize(cxt, partno, xpa, pa);
1423
0
    if (rc)
1424
0
      goto done;
1425
0
  }
1426
1427
0
  DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju)",
1428
0
        partno, xpa,
1429
0
        (uintmax_t) fdisk_partition_get_start(xpa),
1430
0
        (uintmax_t) fdisk_partition_get_end(xpa),
1431
0
        (uintmax_t) fdisk_partition_get_size(xpa)));
1432
1433
  /* disable wipe for old offset/size setting */
1434
0
  if (fdisk_get_partition(cxt, partno, &tmp) == 0 && tmp) {
1435
0
    wipe = fdisk_set_wipe_area(cxt, fdisk_partition_get_start(tmp),
1436
0
            fdisk_partition_get_size(tmp), FALSE);
1437
0
    fdisk_unref_partition(tmp);
1438
0
  }
1439
1440
  /* call label driver */
1441
0
  rc = cxt->label->op->set_part(cxt, partno, xpa);
1442
1443
  /* enable wipe for new offset/size */
1444
0
  if (!rc && wipe)
1445
0
    fdisk_wipe_partition(cxt, partno, TRUE);
1446
0
done:
1447
0
  DBG(CXT, ul_debugobj(cxt, "set_partition() rc=%d", rc));
1448
0
  if (xpa != pa)
1449
0
    fdisk_unref_partition(xpa);
1450
0
  return rc;
1451
0
}
1452
1453
/**
1454
 * fdisk_wipe_partition:
1455
 * @cxt: fdisk context
1456
 * @partno: partition number
1457
 * @enable: 0 or 1
1458
 *
1459
 * Enable/disable filesystems/RAIDs wiping in area defined by partition start and size.
1460
 *
1461
 * Returns: <0 in case of error, 0 on success
1462
 * Since: 2.29
1463
 */
1464
int fdisk_wipe_partition(struct fdisk_context *cxt, size_t partno, int enable)
1465
0
{
1466
0
  struct fdisk_partition *pa = NULL;
1467
0
  int rc;
1468
1469
0
  rc = fdisk_get_partition(cxt, partno, &pa);
1470
0
  if (rc)
1471
0
    return rc;
1472
1473
0
  rc = fdisk_set_wipe_area(cxt, fdisk_partition_get_start(pa),
1474
0
              fdisk_partition_get_size(pa), enable);
1475
0
  fdisk_unref_partition(pa);
1476
0
  return rc < 0 ? rc : 0;
1477
0
}
1478
1479
/**
1480
 * fdisk_partition_has_wipe:
1481
 * @cxt: fdisk context
1482
 * @pa: partition
1483
 *
1484
 * Since: 2.30
1485
 *
1486
 * Returns: 1 if the area specified by @pa will be wiped by write command, or 0.
1487
 */
1488
int fdisk_partition_has_wipe(struct fdisk_context *cxt, struct fdisk_partition *pa)
1489
0
{
1490
0
  return fdisk_has_wipe_area(cxt, fdisk_partition_get_start(pa),
1491
0
          fdisk_partition_get_size(pa));
1492
0
}
1493
1494
1495
/**
1496
 * fdisk_add_partition:
1497
 * @cxt: fdisk context
1498
 * @pa: template for the partition (or NULL)
1499
 * @partno: NULL or returns new partition number
1500
 *
1501
 * If @pa is not specified or any @pa item is missing the libfdisk will ask by
1502
 * fdisk_ask_ API.
1503
 *
1504
 * The @pa template is important for non-interactive partitioning,
1505
 * especially for MBR where is necessary to differentiate between
1506
 * primary/logical; this is done by start offset or/and partno.
1507
 * The rules for MBR:
1508
 *
1509
 *   A) template specifies start within extended partition: add logical
1510
 *   B) template specifies start out of extended partition: add primary
1511
 *   C) template specifies start (or default), partno < 4: add primary
1512
 *   D) template specifies default start, partno >= 4: add logical
1513
 *
1514
 * otherwise MBR driver uses Ask-API to get missing information.
1515
 *
1516
 * Adds a new partition to disklabel.
1517
 *
1518
 * Returns: 0 on success, <0 on error.
1519
 */
1520
int fdisk_add_partition(struct fdisk_context *cxt,
1521
      struct fdisk_partition *pa,
1522
      size_t *partno)
1523
0
{
1524
0
  int rc;
1525
1526
0
  if (!cxt || !cxt->label)
1527
0
    return -EINVAL;
1528
0
  if (!cxt->label->op->add_part)
1529
0
    return -ENOSYS;
1530
0
  if (fdisk_missing_geometry(cxt))
1531
0
    return -EINVAL;
1532
1533
0
  if (pa) {
1534
0
    pa->fs_probed = 0;
1535
0
    DBG(CXT, ul_debugobj(cxt, "adding new partition %p", pa));
1536
0
    if (fdisk_partition_has_start(pa))
1537
0
      DBG(CXT, ul_debugobj(cxt, "     start: %ju", (uintmax_t) fdisk_partition_get_start(pa)));
1538
0
    if (fdisk_partition_has_end(pa))
1539
0
      DBG(CXT, ul_debugobj(cxt, "       end: %ju", (uintmax_t) fdisk_partition_get_end(pa)));
1540
0
    if (fdisk_partition_has_size(pa))
1541
0
      DBG(CXT, ul_debugobj(cxt, "      size: %ju", (uintmax_t) fdisk_partition_get_size(pa)));
1542
1543
0
    DBG(CXT, ul_debugobj(cxt,         "  defaults: start=%s, end=%s, partno=%s",
1544
0
          pa->start_follow_default ? "yes" : "no",
1545
0
          pa->end_follow_default ? "yes" : "no",
1546
0
          pa->partno_follow_default ? "yes" : "no"));
1547
0
  } else
1548
0
    DBG(CXT, ul_debugobj(cxt, "adding partition"));
1549
1550
0
  rc = cxt->label->op->add_part(cxt, pa, partno);
1551
1552
0
  DBG(CXT, ul_debugobj(cxt, "add partition done (rc=%d)", rc));
1553
0
  return rc;
1554
0
}
1555
1556
/**
1557
 * fdisk_delete_partition:
1558
 * @cxt: fdisk context
1559
 * @partno: partition number to delete (0 is the first partition)
1560
 *
1561
 * Deletes a @partno partition from disklabel.
1562
 *
1563
 * Returns: 0 on success, <0 on error
1564
 */
1565
int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
1566
0
{
1567
0
  if (!cxt || !cxt->label)
1568
0
    return -EINVAL;
1569
0
  if (!cxt->label->op->del_part)
1570
0
    return -ENOSYS;
1571
1572
0
  fdisk_wipe_partition(cxt, partno, 0);
1573
1574
0
  DBG(CXT, ul_debugobj(cxt, "deleting %s partition number %zd",
1575
0
        cxt->label->name, partno));
1576
0
  return cxt->label->op->del_part(cxt, partno);
1577
0
}
1578
1579
/**
1580
 * fdisk_delete_all_partitions:
1581
 * @cxt: fdisk context
1582
 *
1583
 * Delete all used partitions from disklabel.
1584
 *
1585
 * Returns: 0 on success, otherwise, a corresponding error.
1586
 */
1587
int fdisk_delete_all_partitions(struct fdisk_context *cxt)
1588
0
{
1589
0
  size_t i;
1590
0
  int rc = 0;
1591
1592
0
  if (!cxt || !cxt->label)
1593
0
    return -EINVAL;
1594
1595
0
  for (i = 0; i < cxt->label->nparts_max; i++) {
1596
1597
0
    if (!fdisk_is_partition_used(cxt, i))
1598
0
      continue;
1599
0
    rc = fdisk_delete_partition(cxt, i);
1600
0
    if (rc)
1601
0
      break;
1602
0
  }
1603
1604
0
  return rc;
1605
0
}
1606
1607
/**
1608
 * fdisk_is_partition_used:
1609
 * @cxt: context
1610
 * @n: partition number (0 is the first partition)
1611
 *
1612
 * Check if the partition number @n is used by partition table. This function
1613
 * does not check if the device is used (e.g. mounted) by system!
1614
 *
1615
 * This is faster than fdisk_get_partition() + fdisk_partition_is_used().
1616
 *
1617
 * Returns: 0 or 1
1618
 */
1619
int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n)
1620
0
{
1621
0
  if (!cxt || !cxt->label)
1622
0
    return -EINVAL;
1623
0
  if (!cxt->label->op->part_is_used)
1624
0
    return -ENOSYS;
1625
1626
0
  return cxt->label->op->part_is_used(cxt, n);
1627
0
}
1628
1629
1630
/**
1631
 * fdisk_partition_max_size:
1632
 * @cxt: context
1633
 * @n: partition number (0 is the first partition)
1634
 * @maxsz: returns maximum size of partition
1635
 *
1636
 * Find maximum size the partition can be resized to.
1637
 * Takes into account free space between this partition and the next one.
1638
 *
1639
 * Returns: 0 on success, <0 on error.
1640
 */
1641
int fdisk_partition_get_max_size(struct fdisk_context *cxt, size_t n,
1642
         fdisk_sector_t *maxsz)
1643
0
{
1644
0
  struct fdisk_partition *cur = NULL;
1645
0
  struct fdisk_table *tb = NULL;
1646
0
  int rc;
1647
1648
0
  rc = fdisk_get_partitions(cxt, &tb);
1649
0
  if (rc)
1650
0
    goto out;
1651
1652
0
  rc = fdisk_get_freespaces(cxt, &tb);
1653
0
  if (rc)
1654
0
    goto out;
1655
1656
0
  rc = fdisk_table_sort_partitions(tb, fdisk_partition_cmp_start);
1657
0
  if (rc)
1658
0
    goto out;
1659
1660
0
  cur = fdisk_table_get_partition_by_partno(tb, n);
1661
0
  if (!cur)
1662
0
    goto einval;
1663
1664
0
  if (!fdisk_partition_has_start(cur))
1665
0
    goto einval;
1666
1667
0
  if (resize_get_last_possible(tb, cur,
1668
0
             fdisk_partition_get_start(cur), maxsz))
1669
0
    goto einval;
1670
1671
0
out:
1672
0
  fdisk_unref_partition(cur);
1673
0
  fdisk_unref_table(tb);
1674
1675
0
  return rc;
1676
1677
0
einval:
1678
  rc = -EINVAL;
1679
0
  goto out;
1680
0
}