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