1 #ifdef HAVE_LIBBLKID
2 # include <blkid.h>
3 #endif
4 
5 #include "blkdev.h"
6 #ifdef __linux__
7 # include "partx.h"
8 #endif
9 #include "loopdev.h"
10 #include "fdiskP.h"
11 
12 #include "strutils.h"
13 
14 /**
15  * SECTION: context
16  * @title: Context
17  * @short_description: stores info about device, labels etc.
18  *
19  * The library distinguish between three types of partitioning objects.
20  *
21  * on-disk label data
22  *    - disk label specific
23  *    - probed and read  by disklabel drivers when assign device to the context
24  *      or when switch to another disk label type
25  *    - only fdisk_write_disklabel() modify on-disk data
26  *
27  * in-memory label data
28  *    - generic data and disklabel specific data stored in struct fdisk_label
29  *    - all partitioning operations are based on in-memory data only
30  *
31  * struct fdisk_partition
32  *    - provides abstraction to present partitions to users
33  *    - fdisk_partition is possible to gather to fdisk_table container
34  *    - used as unified template for new partitions
35  *    - used (with fdisk_table) in fdisk scripts
36  *    - the struct fdisk_partition is always completely independent object and
37  *      any change to the object has no effect to in-memory (or on-disk) label data
38  *
39  * Don't forget to inform kernel about changes by fdisk_reread_partition_table()
40  * or more smart fdisk_reread_changes().
41  */
42 
43 /**
44  * fdisk_new_context:
45  *
46  * Returns: newly allocated libfdisk handler
47  */
fdisk_new_context(void)48 struct fdisk_context *fdisk_new_context(void)
49 {
50 	struct fdisk_context *cxt;
51 
52 	cxt = calloc(1, sizeof(*cxt));
53 	if (!cxt)
54 		return NULL;
55 
56 	DBG(CXT, ul_debugobj(cxt, "alloc"));
57 	cxt->dev_fd = -1;
58 	cxt->refcount = 1;
59 
60 	INIT_LIST_HEAD(&cxt->wipes);
61 
62 	/*
63 	 * Allocate label specific structs.
64 	 *
65 	 * This is necessary (for example) to store label specific
66 	 * context setting.
67 	 */
68 	cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
69 	cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
70 	cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
71 	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
72 	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
73 
74 	bindtextdomain(LIBFDISK_TEXTDOMAIN, LOCALEDIR);
75 
76 	return cxt;
77 }
78 
init_nested_from_parent(struct fdisk_context * cxt,int isnew)79 static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
80 {
81 	struct fdisk_context *parent;
82 
83 	assert(cxt);
84 	assert(cxt->parent);
85 
86 	parent = cxt->parent;
87 
88 	INIT_LIST_HEAD(&cxt->wipes);
89 
90 	cxt->alignment_offset = parent->alignment_offset;
91 	cxt->ask_cb =		parent->ask_cb;
92 	cxt->ask_data =		parent->ask_data;
93 	cxt->dev_fd =		parent->dev_fd;
94 	cxt->first_lba =        parent->first_lba;
95 	cxt->firstsector_bufsz = parent->firstsector_bufsz;
96 	cxt->firstsector =	parent->firstsector;
97 	cxt->geom =		parent->geom;
98 	cxt->grain =            parent->grain;
99 	cxt->io_size =          parent->io_size;
100 	cxt->last_lba =		parent->last_lba;
101 	cxt->min_io_size =      parent->min_io_size;
102 	cxt->optimal_io_size =  parent->optimal_io_size;
103 	cxt->phy_sector_size =  parent->phy_sector_size;
104 	cxt->readonly =		parent->readonly;
105 	cxt->script =		parent->script;
106 	fdisk_ref_script(cxt->script);
107 	cxt->sector_size =      parent->sector_size;
108 	cxt->total_sectors =    parent->total_sectors;
109 	cxt->user_geom =	parent->user_geom;
110 	cxt->user_log_sector =	parent->user_log_sector;
111 	cxt->user_pyh_sector =  parent->user_pyh_sector;
112 
113 	/* parent <--> nested independent setting, initialize for new nested
114 	 * contexts only */
115 	if (isnew) {
116 		cxt->listonly =	parent->listonly;
117 		cxt->display_details =	parent->display_details;
118 		cxt->display_in_cyl_units = parent->display_in_cyl_units;
119 		cxt->protect_bootbits = parent->protect_bootbits;
120 	}
121 
122 	free(cxt->dev_model);
123 	cxt->dev_model = NULL;
124 	cxt->dev_model_probed = 0;
125 
126 	return strdup_between_structs(cxt, parent, dev_path);
127 }
128 
129 /**
130  * fdisk_new_nested_context:
131  * @parent: parental context
132  * @name: optional label name (e.g. "bsd")
133  *
134  * Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
135  * The function also probes for the nested label on the device if device is
136  * already assigned to parent.
137  *
138  * The new context is initialized according to @parent and both context shares
139  * some settings and file descriptor to the device. The child propagate some
140  * changes (like fdisk_assign_device()) to parent, but it does not work
141  * vice-versa. The behavior is undefined if you assign another device to
142  * parent.
143  *
144  * Returns: new context for nested partition table.
145  */
fdisk_new_nested_context(struct fdisk_context * parent,const char * name)146 struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
147 				const char *name)
148 {
149 	struct fdisk_context *cxt;
150 	struct fdisk_label *lb = NULL;
151 
152 	assert(parent);
153 
154 	cxt = calloc(1, sizeof(*cxt));
155 	if (!cxt)
156 		return NULL;
157 
158 	DBG(CXT, ul_debugobj(parent, "alloc nested [%p] [name=%s]", cxt, name));
159 	cxt->refcount = 1;
160 
161 	fdisk_ref_context(parent);
162 	cxt->parent = parent;
163 
164 	if (init_nested_from_parent(cxt, 1) != 0) {
165 		cxt->parent = NULL;
166 		fdisk_unref_context(cxt);
167 		return NULL;
168 	}
169 
170 	if (name) {
171 		if (strcasecmp(name, "bsd") == 0)
172 			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
173 		else if (strcasecmp(name, "dos") == 0 || strcasecmp(name, "mbr") == 0)
174 			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
175 	}
176 
177 	if (lb && parent->dev_fd >= 0) {
178 		DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
179 
180 		cxt->label = lb;
181 
182 		if (lb->op->probe(cxt) == 1)
183 			__fdisk_switch_label(cxt, lb);
184 		else {
185 			DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
186 			if (lb->op->deinit)
187 				lb->op->deinit(lb);
188 			cxt->label = NULL;
189 		}
190 	}
191 
192 	return cxt;
193 }
194 
195 
196 /**
197  * fdisk_ref_context:
198  * @cxt: context pointer
199  *
200  * Increments reference counter.
201  */
fdisk_ref_context(struct fdisk_context * cxt)202 void fdisk_ref_context(struct fdisk_context *cxt)
203 {
204 	if (cxt)
205 		cxt->refcount++;
206 }
207 
208 /**
209  * fdisk_get_label:
210  * @cxt: context instance
211  * @name: label name (e.g. "gpt")
212  *
213  * If no @name specified then returns the current context label.
214  *
215  * The label is allocated and maintained within the context #cxt. There is
216  * nothing like reference counting for labels, you cannot deallocate the
217  * label.
218  *
219  * Returns: label struct or NULL in case of error.
220  */
fdisk_get_label(struct fdisk_context * cxt,const char * name)221 struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
222 {
223 	size_t i;
224 
225 	assert(cxt);
226 
227 	if (!name)
228 		return cxt->label;
229 
230 	if (strcasecmp(name, "mbr") == 0)
231 		name = "dos";
232 
233 	for (i = 0; i < cxt->nlabels; i++)
234 		if (cxt->labels[i]
235 		    && strcasecmp(cxt->labels[i]->name, name) == 0)
236 			return cxt->labels[i];
237 
238 	DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
239 	return NULL;
240 }
241 
242 /**
243  * fdisk_next_label:
244  * @cxt: context instance
245  * @lb: returns pointer to the next label
246  *
247  * <informalexample>
248  *   <programlisting>
249  *      // print all supported labels
250  *	struct fdisk_context *cxt = fdisk_new_context();
251  *	struct fdisk_label *lb = NULL;
252  *
253  *	while (fdisk_next_label(cxt, &lb) == 0)
254  *		print("label name: %s\n", fdisk_label_get_name(lb));
255  *	fdisk_unref_context(cxt);
256  *   </programlisting>
257  * </informalexample>
258  *
259  * Returns: <0 in case of error, 0 on success, 1 at the end.
260  */
fdisk_next_label(struct fdisk_context * cxt,struct fdisk_label ** lb)261 int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
262 {
263 	size_t i;
264 	struct fdisk_label *res = NULL;
265 
266 	if (!lb || !cxt)
267 		return -EINVAL;
268 
269 	if (!*lb)
270 		res = cxt->labels[0];
271 	else {
272 		for (i = 1; i < cxt->nlabels; i++) {
273 			if (*lb == cxt->labels[i - 1]) {
274 				res = cxt->labels[i];
275 				break;
276 			}
277 		}
278 	}
279 
280 	*lb = res;
281 	return res ? 0 : 1;
282 }
283 
284 /**
285  * fdisk_get_nlabels:
286  * @cxt: context
287  *
288  * Returns: number of supported label types
289  */
fdisk_get_nlabels(struct fdisk_context * cxt)290 size_t fdisk_get_nlabels(struct fdisk_context *cxt)
291 {
292 	return cxt ? cxt->nlabels : 0;
293 }
294 
__fdisk_switch_label(struct fdisk_context * cxt,struct fdisk_label * lb)295 int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
296 {
297 	if (!lb || !cxt)
298 		return -EINVAL;
299 	if (lb->disabled) {
300 		DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
301 		return -EINVAL;
302 	}
303 	cxt->label = lb;
304 	DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
305 
306 	fdisk_apply_label_device_properties(cxt);
307 	return 0;
308 }
309 
310 /**
311  * fdisk_has_label:
312  * @cxt: fdisk context
313  *
314  * Returns: return 1 if there is label on the device.
315  */
fdisk_has_label(struct fdisk_context * cxt)316 int fdisk_has_label(struct fdisk_context *cxt)
317 {
318 	return cxt && cxt->label;
319 }
320 
321 /**
322  * fdisk_has_protected_bootbits:
323  * @cxt: fdisk context
324  *
325  * Returns: return 1 if boot bits protection enabled.
326  */
fdisk_has_protected_bootbits(struct fdisk_context * cxt)327 int fdisk_has_protected_bootbits(struct fdisk_context *cxt)
328 {
329 	return cxt && cxt->protect_bootbits;
330 }
331 
332 /**
333  * fdisk_enable_bootbits_protection:
334  * @cxt: fdisk context
335  * @enable: 1 or 0
336  *
337  * The library zeroizes all the first sector when create a new disk label by
338  * default.  This function can be used to control this behavior. For now it's
339  * supported for MBR and GPT.
340  *
341  * Returns: 0 on success, < 0 on error.
342  */
fdisk_enable_bootbits_protection(struct fdisk_context * cxt,int enable)343 int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable)
344 {
345 	if (!cxt)
346 		return -EINVAL;
347 	cxt->protect_bootbits = enable ? 1 : 0;
348 	return 0;
349 }
350 /**
351  * fdisk_disable_dialogs
352  * @cxt: fdisk context
353  * @disable: 1 or 0
354  *
355  * The library uses dialog driven partitioning by default.
356  *
357  * Returns: 0 on success, < 0 on error.
358  *
359  * Since: 2.31
360  */
fdisk_disable_dialogs(struct fdisk_context * cxt,int disable)361 int fdisk_disable_dialogs(struct fdisk_context *cxt, int disable)
362 {
363 	if (!cxt)
364 		return -EINVAL;
365 
366 	cxt->no_disalogs = disable;
367 	return 0;
368 }
369 
370 /**
371  * fdisk_has_dialogs
372  * @cxt: fdisk context
373  *
374  * See fdisk_disable_dialogs()
375  *
376  * Returns: 1 if dialog driven partitioning enabled (default), or 0.
377  *
378  * Since: 2.31
379  */
fdisk_has_dialogs(struct fdisk_context * cxt)380 int fdisk_has_dialogs(struct fdisk_context *cxt)
381 {
382 	return cxt->no_disalogs == 0;
383 }
384 
385 /**
386  * fdisk_enable_wipe
387  * @cxt: fdisk context
388  * @enable: 1 or 0
389  *
390  * The library removes all PT/filesystem/RAID signatures before it writes
391  * partition table. The probing area where it looks for signatures is from
392  * the begin of the disk. The device is wiped by libblkid.
393  *
394  * See also fdisk_wipe_partition().
395  *
396  * Returns: 0 on success, < 0 on error.
397  */
fdisk_enable_wipe(struct fdisk_context * cxt,int enable)398 int fdisk_enable_wipe(struct fdisk_context *cxt, int enable)
399 {
400 	if (!cxt)
401 		return -EINVAL;
402 
403 	fdisk_set_wipe_area(cxt, 0, cxt->total_sectors, enable);
404 	return 0;
405 }
406 
407 /**
408  * fdisk_has_wipe
409  * @cxt: fdisk context
410  *
411  * Returns the current wipe setting. See fdisk_enable_wipe().
412  *
413  * Returns: 0 on success, < 0 on error.
414  */
fdisk_has_wipe(struct fdisk_context * cxt)415 int fdisk_has_wipe(struct fdisk_context *cxt)
416 {
417 	if (!cxt)
418 		return 0;
419 
420 	return fdisk_has_wipe_area(cxt, 0, cxt->total_sectors);
421 }
422 
423 
424 /**
425  * fdisk_get_collision
426  * @cxt: fdisk context
427  *
428  * Returns: name of the filesystem or RAID detected on the device or NULL.
429  */
fdisk_get_collision(struct fdisk_context * cxt)430 const char *fdisk_get_collision(struct fdisk_context *cxt)
431 {
432 	return cxt->collision;
433 }
434 
435 /**
436  * fdisk_is_ptcollision:
437  * @cxt: fdisk context
438  *
439  * The collision detected by libblkid (usually another partition table). Note
440  * that libfdisk does not support all partitions tables, so fdisk_has_label()
441  * may return false, but fdisk_is_ptcollision() may return true.
442  *
443  * Since: 2.30
444  *
445  * Returns: 0 or 1
446  */
fdisk_is_ptcollision(struct fdisk_context * cxt)447 int fdisk_is_ptcollision(struct fdisk_context *cxt)
448 {
449 	return cxt->pt_collision;
450 }
451 
452 /**
453  * fdisk_get_npartitions:
454  * @cxt: context
455  *
456  * The maximal number of the partitions depends on disklabel and does not
457  * have to describe the real limit of PT.
458  *
459  * For example the limit for MBR without extend partition is 4, with extended
460  * partition it's unlimited (so the function returns the current number of all
461  * partitions in this case).
462  *
463  * And for example for GPT it depends on space allocated on disk for array of
464  * entry records (usually 128).
465  *
466  * It's fine to use fdisk_get_npartitions() in loops, but don't forget that
467  * partition may be unused (see fdisk_is_partition_used()).
468  *
469  * <informalexample>
470  *   <programlisting>
471  *	struct fdisk_partition *pa = NULL;
472  *	size_t i, nmax = fdisk_get_npartitions(cxt);
473  *
474  *	for (i = 0; i < nmax; i++) {
475  *		if (!fdisk_is_partition_used(cxt, i))
476  *			continue;
477  *		... do something ...
478  *	}
479  *   </programlisting>
480  * </informalexample>
481  *
482  * Note that the recommended way to list partitions is to use
483  * fdisk_get_partitions() and struct fdisk_table then ask disk driver for each
484  * individual partitions.
485  *
486  * Returns: maximal number of partitions for the current label.
487  */
fdisk_get_npartitions(struct fdisk_context * cxt)488 size_t fdisk_get_npartitions(struct fdisk_context *cxt)
489 {
490 	return cxt && cxt->label ? cxt->label->nparts_max : 0;
491 }
492 
493 /**
494  * fdisk_is_labeltype:
495  * @cxt: fdisk context
496  * @id: FDISK_DISKLABEL_*
497  *
498  * See also fdisk_is_label() macro in libfdisk.h.
499  *
500  * Returns: return 1 if the current label is @id
501  */
fdisk_is_labeltype(struct fdisk_context * cxt,enum fdisk_labeltype id)502 int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
503 {
504 	assert(cxt);
505 
506 	return cxt->label && (unsigned)fdisk_label_get_type(cxt->label) == id;
507 }
508 
509 /**
510  * fdisk_get_parent:
511  * @cxt: nested fdisk context
512  *
513  * Returns: pointer to parental context, or NULL
514  */
fdisk_get_parent(struct fdisk_context * cxt)515 struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt)
516 {
517 	assert(cxt);
518 	return cxt->parent;
519 }
520 
reset_context(struct fdisk_context * cxt)521 static void reset_context(struct fdisk_context *cxt)
522 {
523 	size_t i;
524 
525 	DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
526 
527 	/* reset drives' private data */
528 	for (i = 0; i < cxt->nlabels; i++)
529 		fdisk_deinit_label(cxt->labels[i]);
530 
531 	if (cxt->parent) {
532 		/* the first sector may be independent on parent */
533 		if (cxt->parent->firstsector != cxt->firstsector) {
534 			DBG(CXT, ul_debugobj(cxt, "  firstsector independent on parent (freeing)"));
535 			free(cxt->firstsector);
536 		}
537 	} else {
538 		/* we close device only in primary context */
539 		if (cxt->dev_fd > -1 && cxt->private_fd)
540 			close(cxt->dev_fd);
541 		DBG(CXT, ul_debugobj(cxt, "  freeing firstsector"));
542 		free(cxt->firstsector);
543 	}
544 
545 	free(cxt->dev_path);
546 	cxt->dev_path = NULL;
547 
548 	free(cxt->dev_model);
549 	cxt->dev_model = NULL;
550 	cxt->dev_model_probed = 0;
551 
552 	free(cxt->collision);
553 	cxt->collision = NULL;
554 
555 	memset(&cxt->dev_st, 0, sizeof(cxt->dev_st));
556 
557 	cxt->dev_fd = -1;
558 	cxt->private_fd = 0;
559 	cxt->firstsector = NULL;
560 	cxt->firstsector_bufsz = 0;
561 
562 	fdisk_zeroize_device_properties(cxt);
563 
564 	fdisk_unref_script(cxt->script);
565 	cxt->script = NULL;
566 
567 	cxt->label = NULL;
568 
569 	fdisk_free_wipe_areas(cxt);
570 }
571 
572 /* fdisk_assign_device() body */
fdisk_assign_fd(struct fdisk_context * cxt,int fd,const char * fname,int readonly,int privfd)573 static int fdisk_assign_fd(struct fdisk_context *cxt, int fd,
574 			const char *fname, int readonly, int privfd)
575 {
576 	assert(cxt);
577 	assert(fd >= 0);
578 
579 	/* redirect request to parent */
580 	if (cxt->parent) {
581 		int rc, org = fdisk_is_listonly(cxt->parent);
582 
583 		/* assign_device() is sensitive to "listonly" mode, so let's
584 		 * follow the current context setting for the parent to avoid
585 		 * unwanted extra warnings. */
586 		fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
587 
588 		rc = fdisk_assign_fd(cxt->parent, fd, fname, readonly, privfd);
589 		fdisk_enable_listonly(cxt->parent, org);
590 
591 		if (!rc)
592 			rc = init_nested_from_parent(cxt, 0);
593 		if (!rc)
594 			fdisk_probe_labels(cxt);
595 		return rc;
596 	}
597 
598 	reset_context(cxt);
599 
600 	if (fstat(fd, &cxt->dev_st) != 0)
601 		goto fail;
602 
603 	cxt->readonly = readonly;
604 	cxt->dev_fd = fd;
605 	cxt->private_fd = privfd;
606 	cxt->dev_path = fname ? strdup(fname) : NULL;
607 	if (!cxt->dev_path)
608 		goto fail;
609 
610 	fdisk_discover_topology(cxt);
611 	fdisk_discover_geometry(cxt);
612 
613 	fdisk_apply_user_device_properties(cxt);
614 
615 	if (fdisk_read_firstsector(cxt) < 0)
616 		goto fail;
617 
618 	/* warn about obsolete stuff on the device if we aren't in list-only */
619 	if (!fdisk_is_listonly(cxt) && fdisk_check_collisions(cxt) < 0)
620 		goto fail;
621 
622 	fdisk_probe_labels(cxt);
623 	fdisk_apply_label_device_properties(cxt);
624 
625 	/* Don't report collision if there is already a valid partition table.
626 	 * The bootbits are wiped when we create a *new* partition table only. */
627 	if (fdisk_is_ptcollision(cxt) && fdisk_has_label(cxt)) {
628 		cxt->pt_collision = 0;
629 		free(cxt->collision);
630 		cxt->collision = NULL;
631 	}
632 
633 	DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
634 			      fname, readonly ? "READ-ONLY" : "READ-WRITE"));
635 	return 0;
636 fail:
637 	{
638 		int rc = -errno;
639 		DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc));
640 		return rc;
641 	}
642 }
643 
644 /**
645  * fdisk_assign_device:
646  * @cxt: context
647  * @fname: path to the device to be handled
648  * @readonly: how to open the device
649  *
650  * Open the device, discovery topology, geometry, detect disklabel, check for
651  * collisions and switch the current label driver to reflect the probing
652  * result.
653  *
654  * If in standard mode (!= non-listonly mode) then also detects for collisions.
655  * The result is accessible by fdisk_get_collision() and
656  * fdisk_is_ptcollision().  The collision (e.g. old obsolete PT) may be removed
657  * by fdisk_enable_wipe().  Note that new PT and old PT may be on different
658  * locations.
659  *
660  * Note that this function resets all generic setting in context.
661  *
662  * If the @cxt is nested context (necessary for example to edit BSD or PMBR)
663  * then the device is assigned to the parental context and necessary properties
664  * are copied to the @cxt. The change is propagated in child->parent direction
665  * only. It's impossible to use a different device for primary and nested
666  * contexts.
667  *
668  * Returns: 0 on success, < 0 on error.
669  */
fdisk_assign_device(struct fdisk_context * cxt,const char * fname,int readonly)670 int fdisk_assign_device(struct fdisk_context *cxt,
671 			const char *fname, int readonly)
672 {
673 	int fd, rc;
674 
675 	DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
676 	assert(cxt);
677 
678 	fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
679 	if (fd < 0) {
680 		rc = -errno;
681 		DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc));
682 		return rc;
683 	}
684 
685 	rc = fdisk_assign_fd(cxt, fd, fname, readonly, 1);
686 	if (rc)
687 		close(fd);
688 	return rc;
689 }
690 
691 /**
692  * fdisk_assign_device_by_fd:
693  * @cxt: context
694  * @fd: device file descriptor
695  * @fname: path to the device (used for dialogs, debugging, partition names, ...)
696  * @readonly: how to use the device
697  *
698  * Like fdisk_assign_device(), but caller is responsible to open and close the
699  * device. The library only fsync() the device on fdisk_deassign_device().
700  *
701  * The device has to be open O_RDWR on @readonly=0.
702  *
703  * Returns: 0 on success, < 0 on error.
704  *
705  * Since: 2.35
706  */
fdisk_assign_device_by_fd(struct fdisk_context * cxt,int fd,const char * fname,int readonly)707 int fdisk_assign_device_by_fd(struct fdisk_context *cxt, int fd,
708 			const char *fname, int readonly)
709 {
710 	return fdisk_assign_fd(cxt, fd, fname, readonly, 0);
711 }
712 
713 /**
714  * fdisk_deassign_device:
715  * @cxt: context
716  * @nosync: disable sync() after close().
717  *
718  * Call fsync(), close() and than sync(), but for read-only handler close the
719  * device only. If the @cxt is nested context then the request is redirected to
720  * the parent.
721  *
722  * Returns: 0 on success, < 0 on error.
723  */
fdisk_deassign_device(struct fdisk_context * cxt,int nosync)724 int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
725 {
726 	assert(cxt);
727 	assert(cxt->dev_fd >= 0);
728 
729 	if (cxt->parent) {
730 		int rc = fdisk_deassign_device(cxt->parent, nosync);
731 
732 		if (!rc)
733 			rc = init_nested_from_parent(cxt, 0);
734 		return rc;
735 	}
736 
737 	DBG(CXT, ul_debugobj(cxt, "de-assigning device %s", cxt->dev_path));
738 
739 	if (cxt->readonly && cxt->private_fd)
740 		close(cxt->dev_fd);
741 	else {
742 		if (fsync(cxt->dev_fd)) {
743 			fdisk_warn(cxt, _("%s: fsync device failed"),
744 					cxt->dev_path);
745 			return -errno;
746 		}
747 		if (cxt->private_fd && close(cxt->dev_fd)) {
748 			fdisk_warn(cxt, _("%s: close device failed"),
749 					cxt->dev_path);
750 			return -errno;
751 		}
752 		if (!nosync) {
753 			fdisk_info(cxt, _("Syncing disks."));
754 			sync();
755 		}
756 	}
757 
758 	free(cxt->dev_path);
759 	cxt->dev_path = NULL;
760 	cxt->dev_fd = -1;
761 
762 	return 0;
763 }
764 
765 /**
766  * fdisk_reassign_device:
767  * @cxt: context
768  *
769  * This function is "hard reset" of the context and it does not write anything
770  * to the device. All in-memory changes associated with the context will be
771  * lost. It's recommended to use this function after some fatal problem when the
772  * context (and label specific driver) is in an undefined state.
773  *
774  * Returns: 0 on success, < 0 on error.
775  */
fdisk_reassign_device(struct fdisk_context * cxt)776 int fdisk_reassign_device(struct fdisk_context *cxt)
777 {
778 	char *devname;
779 	int rdonly, rc, fd, privfd;
780 
781 	assert(cxt);
782 	assert(cxt->dev_fd >= 0);
783 
784 	DBG(CXT, ul_debugobj(cxt, "re-assigning device %s", cxt->dev_path));
785 
786 	devname = strdup(cxt->dev_path);
787 	if (!devname)
788 		return -ENOMEM;
789 
790 	rdonly = cxt->readonly;
791 	fd = cxt->dev_fd;
792 	privfd = cxt->private_fd;
793 
794 	fdisk_deassign_device(cxt, 1);
795 
796 	if (privfd)
797 		/* reopen and assign */
798 		rc = fdisk_assign_device(cxt, devname, rdonly);
799 	else
800 		/* assign only */
801 		rc = fdisk_assign_fd(cxt, fd, devname, rdonly, privfd);
802 
803 	free(devname);
804 	return rc;
805 }
806 
807 /**
808  * fdisk_reread_partition_table:
809  * @cxt: context
810  *
811  * Force *kernel* to re-read partition table on block devices.
812  *
813  * Returns: 0 on success, < 0 in case of error.
814  */
fdisk_reread_partition_table(struct fdisk_context * cxt)815 int fdisk_reread_partition_table(struct fdisk_context *cxt)
816 {
817 	int i = 0;
818 
819 	assert(cxt);
820 	assert(cxt->dev_fd >= 0);
821 
822 	if (!S_ISBLK(cxt->dev_st.st_mode))
823 		return 0;
824 
825 	DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl"));
826 	sync();
827 #ifdef BLKRRPART
828 	fdisk_info(cxt, _("Calling ioctl() to re-read partition table."));
829 	i = ioctl(cxt->dev_fd, BLKRRPART);
830 #else
831 	errno = ENOSYS;
832 	i = 1;
833 #endif
834 
835 	if (i) {
836 		fdisk_warn(cxt, _("Re-reading the partition table failed."));
837 		fdisk_info(cxt,	_(
838 			"The kernel still uses the old table. The "
839 			"new table will be used at the next reboot "
840 			"or after you run partprobe(8) or partx(8)."));
841 		return -errno;
842 	}
843 
844 	return 0;
845 }
846 
847 #ifdef __linux__
add_to_partitions_array(struct fdisk_partition *** ary,struct fdisk_partition * pa,size_t * n,size_t nmax)848 static inline int add_to_partitions_array(
849 			struct fdisk_partition ***ary,
850 			struct fdisk_partition *pa,
851 			size_t *n, size_t nmax)
852 {
853 	if (!*ary) {
854 		*ary = calloc(nmax, sizeof(struct fdisk_partition *));
855 		if (!*ary)
856 			return -ENOMEM;
857 	}
858 	(*ary)[*n] = pa;
859 	(*n)++;
860 	return 0;
861 }
862 #endif
863 
864 /**
865  * fdisk_reread_changes:
866  * @cxt: context
867  * @org: original layout (on disk)
868  *
869  * Like fdisk_reread_partition_table() but don't forces kernel re-read all
870  * partition table. The BLKPG_* ioctls are used for individual partitions. The
871  * advantage is that unmodified partitions maybe mounted.
872  *
873  * The function behaves like fdisk_reread_partition_table() on systems where
874  * are no available BLKPG_* ioctls.
875  *
876  * Returns: <0 on error, or 0.
877  */
878 #ifdef __linux__
fdisk_reread_changes(struct fdisk_context * cxt,struct fdisk_table * org)879 int fdisk_reread_changes(struct fdisk_context *cxt, struct fdisk_table *org)
880 {
881 	struct fdisk_table *tb = NULL;
882 	struct fdisk_iter itr;
883 	struct fdisk_partition *pa;
884 	struct fdisk_partition **rem = NULL, **add = NULL, **upd = NULL;
885 	int change, rc = 0, err = 0;
886 	size_t nparts, i, nadds = 0, nupds = 0, nrems = 0;
887 	unsigned int ssf;
888 
889 	DBG(CXT, ul_debugobj(cxt, "rereading changes"));
890 
891 	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
892 
893 	/* the current layout */
894 	fdisk_get_partitions(cxt, &tb);
895 	/* maximal number of partitions */
896 	nparts = max(fdisk_table_get_nents(tb), fdisk_table_get_nents(org));
897 
898 	while (fdisk_diff_tables(org, tb, &itr, &pa, &change) == 0) {
899 		if (change == FDISK_DIFF_UNCHANGED)
900 			continue;
901 		switch (change) {
902 		case FDISK_DIFF_REMOVED:
903 			rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
904 			break;
905 		case FDISK_DIFF_ADDED:
906 			rc = add_to_partitions_array(&add, pa, &nadds, nparts);
907 			break;
908 		case FDISK_DIFF_RESIZED:
909 			rc = add_to_partitions_array(&upd, pa, &nupds, nparts);
910 			break;
911 		case FDISK_DIFF_MOVED:
912 			rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
913 			if (!rc)
914 				rc = add_to_partitions_array(&add, pa, &nadds, nparts);
915 			break;
916 		}
917 		if (rc != 0)
918 			goto done;
919 	}
920 
921 	/* sector size factor -- used to recount from real to 512-byte sectors */
922 	ssf = cxt->sector_size / 512;
923 
924 	for (i = 0; i < nrems; i++) {
925 		pa = rem[i];
926 		DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_DEL_PARTITION", pa->partno));
927 		if (partx_del_partition(cxt->dev_fd, pa->partno + 1) != 0) {
928 			fdisk_warn(cxt, _("Failed to remove partition %zu from system"), pa->partno + 1);
929 			err++;
930 		}
931 	}
932 	for (i = 0; i < nupds; i++) {
933 		pa = upd[i];
934 		DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_RESIZE_PARTITION", pa->partno));
935 		if (partx_resize_partition(cxt->dev_fd, pa->partno + 1,
936 					   pa->start * ssf, pa->size * ssf) != 0) {
937 			fdisk_warn(cxt, _("Failed to update system information about partition %zu"), pa->partno + 1);
938 			err++;
939 		}
940 	}
941 	for (i = 0; i < nadds; i++) {
942 		pa = add[i];
943 		DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_ADD_PARTITION", pa->partno));
944 		if (partx_add_partition(cxt->dev_fd, pa->partno + 1,
945 					pa->start * ssf, pa->size * ssf) != 0) {
946 			fdisk_warn(cxt, _("Failed to add partition %zu to system"), pa->partno + 1);
947 			err++;
948 		}
949 	}
950 	if (err)
951 		fdisk_info(cxt,	_(
952 			"The kernel still uses the old partitions. The new "
953 			"table will be used at the next reboot. "));
954 done:
955 	free(rem);
956 	free(add);
957 	free(upd);
958 	fdisk_unref_table(tb);
959 	return rc;
960 }
961 #else
fdisk_reread_changes(struct fdisk_context * cxt,struct fdisk_table * org)962 int fdisk_reread_changes(struct fdisk_context *cxt,
963 			 struct fdisk_table *org __attribute__((__unused__))) {
964 	return fdisk_reread_partition_table(cxt);
965 }
966 #endif
967 
968 /**
969  * fdisk_device_is_used:
970  * @cxt: context
971  *
972  * On systems where is no BLKRRPART ioctl the function returns zero and
973  * sets errno to ENOSYS.
974  *
975  * Returns: 1 if the device assigned to the context is used by system, or 0.
976  */
fdisk_device_is_used(struct fdisk_context * cxt)977 int fdisk_device_is_used(struct fdisk_context *cxt)
978 {
979 	int rc = 0;
980 
981 	assert(cxt);
982 	assert(cxt->dev_fd >= 0);
983 
984 	errno = 0;
985 
986 #ifdef BLKRRPART
987 	/* it seems kernel always return EINVAL for BLKRRPART on loopdevices */
988 	if (S_ISBLK(cxt->dev_st.st_mode)
989 	    && major(cxt->dev_st.st_rdev) != LOOPDEV_MAJOR) {
990 		DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl"));
991 		rc = ioctl(cxt->dev_fd, BLKRRPART) != 0;
992 	}
993 #else
994 	errno = ENOSYS;
995 #endif
996 	DBG(CXT, ul_debugobj(cxt, "device used: %s [errno=%d]", rc ? "TRUE" : "FALSE", errno));
997 	return rc;
998 }
999 
1000 /**
1001  * fdisk_is_readonly:
1002  * @cxt: context
1003  *
1004  * Returns: 1 if device open readonly
1005  */
fdisk_is_readonly(struct fdisk_context * cxt)1006 int fdisk_is_readonly(struct fdisk_context *cxt)
1007 {
1008 	assert(cxt);
1009 	return cxt->readonly;
1010 }
1011 
1012 /**
1013  * fdisk_is_regfile:
1014  * @cxt: context
1015  *
1016  * Since: 2.30
1017  *
1018  * Returns: 1 if open file descriptor is regular file rather than a block device.
1019  */
fdisk_is_regfile(struct fdisk_context * cxt)1020 int fdisk_is_regfile(struct fdisk_context *cxt)
1021 {
1022 	assert(cxt);
1023 	return S_ISREG(cxt->dev_st.st_mode);
1024 }
1025 
1026 /**
1027  * fdisk_unref_context:
1028  * @cxt: fdisk context
1029  *
1030  * Deallocates context struct.
1031  */
fdisk_unref_context(struct fdisk_context * cxt)1032 void fdisk_unref_context(struct fdisk_context *cxt)
1033 {
1034 	unsigned i;
1035 
1036 	if (!cxt)
1037 		return;
1038 
1039 	cxt->refcount--;
1040 	if (cxt->refcount <= 0) {
1041 		DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
1042 
1043 		reset_context(cxt);	/* this is sensitive to parent<->child relationship! */
1044 
1045 		/* deallocate label's private stuff */
1046 		for (i = 0; i < cxt->nlabels; i++) {
1047 			if (!cxt->labels[i])
1048 				continue;
1049 			if (cxt->labels[i]->op->free)
1050 				cxt->labels[i]->op->free(cxt->labels[i]);
1051 			else
1052 				free(cxt->labels[i]);
1053 			cxt->labels[i] = NULL;
1054 		}
1055 
1056 		fdisk_unref_context(cxt->parent);
1057 		cxt->parent = NULL;
1058 
1059 		free(cxt);
1060 	}
1061 }
1062 
1063 
1064 /**
1065  * fdisk_enable_details:
1066  * @cxt: context
1067  * @enable: true/false
1068  *
1069  * Enables or disables "details" display mode. This function has effect to
1070  * fdisk_partition_to_string() function.
1071  *
1072  * Returns: 0 on success, < 0 on error.
1073  */
fdisk_enable_details(struct fdisk_context * cxt,int enable)1074 int fdisk_enable_details(struct fdisk_context *cxt, int enable)
1075 {
1076 	assert(cxt);
1077 	cxt->display_details = enable ? 1 : 0;
1078 	return 0;
1079 }
1080 
1081 /**
1082  * fdisk_is_details:
1083  * @cxt: context
1084  *
1085  * Returns: 1 if details are enabled
1086  */
fdisk_is_details(struct fdisk_context * cxt)1087 int fdisk_is_details(struct fdisk_context *cxt)
1088 {
1089 	assert(cxt);
1090 	return cxt->display_details == 1;
1091 }
1092 
1093 /**
1094  * fdisk_enable_listonly:
1095  * @cxt: context
1096  * @enable: true/false
1097  *
1098  * Just list partition only, don't care about another details, mistakes, ...
1099  *
1100  * Returns: 0 on success, < 0 on error.
1101  */
fdisk_enable_listonly(struct fdisk_context * cxt,int enable)1102 int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
1103 {
1104 	assert(cxt);
1105 	cxt->listonly = enable ? 1 : 0;
1106 	return 0;
1107 }
1108 
1109 /**
1110  * fdisk_is_listonly:
1111  * @cxt: context
1112  *
1113  * Returns: 1 if list-only mode enabled
1114  */
fdisk_is_listonly(struct fdisk_context * cxt)1115 int fdisk_is_listonly(struct fdisk_context *cxt)
1116 {
1117 	assert(cxt);
1118 	return cxt->listonly == 1;
1119 }
1120 
1121 
1122 /**
1123  * fdisk_set_unit:
1124  * @cxt: context
1125  * @str: "cylinder" or "sector".
1126  *
1127  * This is pure shit, unfortunately for example Sun addresses begin of the
1128  * partition by cylinders...
1129  *
1130  * Returns: 0 on success, <0 on error.
1131  */
fdisk_set_unit(struct fdisk_context * cxt,const char * str)1132 int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
1133 {
1134 	assert(cxt);
1135 
1136 	cxt->display_in_cyl_units = 0;
1137 
1138 	if (!str)
1139 		return 0;
1140 
1141 	if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
1142 		cxt->display_in_cyl_units = 1;
1143 
1144 	else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
1145 		cxt->display_in_cyl_units = 0;
1146 
1147 	DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
1148 	return 0;
1149 }
1150 
1151 /**
1152  * fdisk_get_unit:
1153  * @cxt: context
1154  * @n: FDISK_PLURAL or FDISK_SINGULAR
1155  *
1156  * Returns: unit name.
1157  */
fdisk_get_unit(struct fdisk_context * cxt,int n)1158 const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
1159 {
1160 	assert(cxt);
1161 
1162 	if (fdisk_use_cylinders(cxt))
1163 		return P_("cylinder", "cylinders", n);
1164 	return P_("sector", "sectors", n);
1165 }
1166 
1167 /**
1168  * fdisk_use_cylinders:
1169  * @cxt: context
1170  *
1171  * Returns: 1 if user wants to display in cylinders.
1172  */
fdisk_use_cylinders(struct fdisk_context * cxt)1173 int fdisk_use_cylinders(struct fdisk_context *cxt)
1174 {
1175 	assert(cxt);
1176 	return cxt->display_in_cyl_units == 1;
1177 }
1178 
1179 /**
1180  * fdisk_get_units_per_sector:
1181  * @cxt: context
1182  *
1183  * This is necessary only for brain dead situations when we use "cylinders";
1184  *
1185  * Returns: number of "units" per sector, default is 1 if display unit is sector.
1186  */
fdisk_get_units_per_sector(struct fdisk_context * cxt)1187 unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
1188 {
1189 	assert(cxt);
1190 
1191 	if (fdisk_use_cylinders(cxt)) {
1192 		assert(cxt->geom.heads);
1193 		return cxt->geom.heads * cxt->geom.sectors;
1194 	}
1195 	return 1;
1196 }
1197 
1198 /**
1199  * fdisk_get_optimal_iosize:
1200  * @cxt: context
1201  *
1202  * The optimal I/O is optional and does not have to be provided by device,
1203  * anyway libfdisk never returns zero. If the optimal I/O size is not provided
1204  * then libfdisk returns minimal I/O size or sector size.
1205  *
1206  * Returns: optimal I/O size in bytes.
1207  */
fdisk_get_optimal_iosize(struct fdisk_context * cxt)1208 unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
1209 {
1210 	assert(cxt);
1211 	return cxt->optimal_io_size ? cxt->optimal_io_size : cxt->io_size;
1212 }
1213 
1214 /**
1215  * fdisk_get_minimal_iosize:
1216  * @cxt: context
1217  *
1218  * Returns: minimal I/O size in bytes
1219  */
fdisk_get_minimal_iosize(struct fdisk_context * cxt)1220 unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt)
1221 {
1222 	assert(cxt);
1223 	return cxt->min_io_size;
1224 }
1225 
1226 /**
1227  * fdisk_get_physector_size:
1228  * @cxt: context
1229  *
1230  * Returns: physical sector size in bytes
1231  */
fdisk_get_physector_size(struct fdisk_context * cxt)1232 unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
1233 {
1234 	assert(cxt);
1235 	return cxt->phy_sector_size;
1236 }
1237 
1238 /**
1239  * fdisk_get_sector_size:
1240  * @cxt: context
1241  *
1242  * Returns: logical sector size in bytes
1243  */
fdisk_get_sector_size(struct fdisk_context * cxt)1244 unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
1245 {
1246 	assert(cxt);
1247 	return cxt->sector_size;
1248 }
1249 
1250 /**
1251  * fdisk_get_alignment_offset
1252  * @cxt: context
1253  *
1254  * The alignment offset is offset between logical and physical sectors. For
1255  * backward compatibility the first logical sector on 4K disks does no have to
1256  * start on the same place like physical sectors.
1257  *
1258  * Returns: alignment offset in bytes
1259  */
fdisk_get_alignment_offset(struct fdisk_context * cxt)1260 unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
1261 {
1262 	assert(cxt);
1263 	return cxt->alignment_offset;
1264 }
1265 
1266 /**
1267  * fdisk_get_grain_size:
1268  * @cxt: context
1269  *
1270  * Returns: grain in bytes used to align partitions (usually 1MiB)
1271  */
fdisk_get_grain_size(struct fdisk_context * cxt)1272 unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
1273 {
1274 	assert(cxt);
1275 	return cxt->grain;
1276 }
1277 
1278 /**
1279  * fdisk_get_first_lba:
1280  * @cxt: context
1281  *
1282  * Returns: first possible LBA on disk for data partitions.
1283  */
fdisk_get_first_lba(struct fdisk_context * cxt)1284 fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
1285 {
1286 	assert(cxt);
1287 	return cxt->first_lba;
1288 }
1289 
1290 /**
1291  * fdisk_set_first_lba:
1292  * @cxt: fdisk context
1293  * @lba: first possible logical sector for data
1294  *
1295  * It's strongly recommended to use the default library setting. The first LBA
1296  * is always reset by fdisk_assign_device(), fdisk_override_geometry()
1297  * and fdisk_reset_alignment(). This is very low level function and library
1298  * does not check if your setting makes any sense.
1299  *
1300  * This function is necessary only when you want to work with very unusual
1301  * partition tables like GPT protective MBR or hybrid partition tables on
1302  * bootable media where the first partition may start on very crazy offsets.
1303  *
1304  * Note that this function changes only runtime information. It does not update
1305  * any range in on-disk partition table. For example GPT Header contains First
1306  * and Last usable LBA fields. These fields are not updated by this function.
1307  * Be careful.
1308  *
1309  * Returns: 0 on success, <0 on error.
1310  */
fdisk_set_first_lba(struct fdisk_context * cxt,fdisk_sector_t lba)1311 fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
1312 {
1313 	assert(cxt);
1314 	DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
1315 			(uintmax_t) cxt->first_lba, (uintmax_t) lba));
1316 	cxt->first_lba = lba;
1317 	return 0;
1318 }
1319 
1320 /**
1321  * fdisk_get_last_lba:
1322  * @cxt: fdisk context
1323  *
1324  * Note that the device has to be already assigned.
1325  *
1326  * Returns: last possible LBA on device
1327  */
fdisk_get_last_lba(struct fdisk_context * cxt)1328 fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
1329 {
1330 	return cxt->last_lba;
1331 }
1332 
1333 /**
1334  * fdisk_set_last_lba:
1335  * @cxt: fdisk context
1336  * @lba: last possible logical sector
1337  *
1338  * It's strongly recommended to use the default library setting. The last LBA
1339  * is always reset by fdisk_assign_device(), fdisk_override_geometry() and
1340  * fdisk_reset_alignment().
1341  *
1342  * The default is number of sectors on the device, but maybe modified by the
1343  * current disklabel driver (for example GPT uses the end of disk for backup
1344  * header, so last_lba is smaller than total number of sectors).
1345  *
1346  * Returns: 0 on success, <0 on error.
1347  */
fdisk_set_last_lba(struct fdisk_context * cxt,fdisk_sector_t lba)1348 fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
1349 {
1350 	assert(cxt);
1351 
1352 	if (lba > cxt->total_sectors - 1 || lba < 1)
1353 		return -ERANGE;
1354 	cxt->last_lba = lba;
1355 	return 0;
1356 }
1357 
1358 /**
1359  * fdisk_set_size_unit:
1360  * @cxt: fdisk context
1361  * @unit: FDISK_SIZEUNIT_*
1362  *
1363  * Sets unit for SIZE output field (see fdisk_partition_to_string()).
1364  *
1365  * Returns: 0 on success, <0 on error.
1366  */
fdisk_set_size_unit(struct fdisk_context * cxt,int unit)1367 int fdisk_set_size_unit(struct fdisk_context *cxt, int unit)
1368 {
1369 	assert(cxt);
1370 	cxt->sizeunit = unit;
1371 	return 0;
1372 }
1373 
1374 /**
1375  * fdisk_get_size_unit:
1376  * @cxt: fdisk context
1377  *
1378  * Gets unit for SIZE output field (see fdisk_partition_to_string()).
1379  *
1380  * Returns: unit
1381  */
fdisk_get_size_unit(struct fdisk_context * cxt)1382 int fdisk_get_size_unit(struct fdisk_context *cxt)
1383 {
1384 	assert(cxt);
1385 	return cxt->sizeunit;
1386 }
1387 
1388 /**
1389  * fdisk_get_nsectors:
1390  * @cxt: context
1391  *
1392  * Returns: size of the device in logical sectors.
1393  */
fdisk_get_nsectors(struct fdisk_context * cxt)1394 fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
1395 {
1396 	assert(cxt);
1397 	return cxt->total_sectors;
1398 }
1399 
1400 /**
1401  * fdisk_get_devname:
1402  * @cxt: context
1403  *
1404  * Returns: device name.
1405  */
fdisk_get_devname(struct fdisk_context * cxt)1406 const char *fdisk_get_devname(struct fdisk_context *cxt)
1407 {
1408 	assert(cxt);
1409 	return cxt->dev_path;
1410 }
1411 
1412 /**
1413  * fdisk_get_devno:
1414  * @cxt: context
1415  *
1416  * Returns: device number or zero for non-block devices
1417  *
1418  * Since: 2.33
1419  */
fdisk_get_devno(struct fdisk_context * cxt)1420 dev_t fdisk_get_devno(struct fdisk_context *cxt)
1421 {
1422 	assert(cxt);
1423 	return S_ISBLK(cxt->dev_st.st_mode) ? cxt->dev_st.st_rdev : 0;
1424 }
1425 
1426 /**
1427  * fdisk_get_devmodel:
1428  * @cxt: context
1429  *
1430  * Returns: device model string or NULL.
1431  *
1432  * Since: 2.33
1433  */
1434 #ifdef __linux__
fdisk_get_devmodel(struct fdisk_context * cxt)1435 const char *fdisk_get_devmodel(struct fdisk_context *cxt)
1436 {
1437 	assert(cxt);
1438 
1439 	if (cxt->dev_model_probed)
1440 		return cxt->dev_model;
1441 
1442 	if (fdisk_get_devno(cxt)) {
1443 		struct path_cxt *pc = ul_new_sysfs_path(fdisk_get_devno(cxt), NULL, NULL);
1444 
1445 		if (pc) {
1446 			ul_path_read_string(pc, &cxt->dev_model, "device/model");
1447 			ul_unref_path(pc);
1448 		}
1449 	}
1450 	cxt->dev_model_probed = 1;
1451 	return cxt->dev_model;
1452 }
1453 #else
fdisk_get_devmodel(struct fdisk_context * cxt)1454 const char *fdisk_get_devmodel(struct fdisk_context *cxt __attribute__((__unused__)))
1455 {
1456 	return NULL;
1457 }
1458 #endif
1459 
1460 /**
1461  * fdisk_get_devfd:
1462  * @cxt: context
1463  *
1464  * Returns: device file descriptor.
1465  */
fdisk_get_devfd(struct fdisk_context * cxt)1466 int fdisk_get_devfd(struct fdisk_context *cxt)
1467 {
1468 	assert(cxt);
1469 	return cxt->dev_fd;
1470 }
1471 
1472 /**
1473  * fdisk_get_geom_heads:
1474  * @cxt: context
1475  *
1476  * Returns: number of geometry heads.
1477  */
fdisk_get_geom_heads(struct fdisk_context * cxt)1478 unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
1479 {
1480 	assert(cxt);
1481 	return cxt->geom.heads;
1482 }
1483 /**
1484  * fdisk_get_geom_sectors:
1485  * @cxt: context
1486  *
1487  * Returns: number of geometry sectors.
1488  */
fdisk_get_geom_sectors(struct fdisk_context * cxt)1489 fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
1490 {
1491 	assert(cxt);
1492 	return cxt->geom.sectors;
1493 
1494 }
1495 
1496 /**
1497  * fdisk_get_geom_cylinders:
1498  * @cxt: context
1499  *
1500  * Returns: number of geometry cylinders
1501  */
fdisk_get_geom_cylinders(struct fdisk_context * cxt)1502 fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
1503 {
1504 	assert(cxt);
1505 	return cxt->geom.cylinders;
1506 }
1507 
fdisk_missing_geometry(struct fdisk_context * cxt)1508 int fdisk_missing_geometry(struct fdisk_context *cxt)
1509 {
1510 	int rc;
1511 
1512 	if (!cxt || !cxt->label)
1513 		return 0;
1514 
1515 	rc = (fdisk_label_require_geometry(cxt->label) &&
1516 		    (!cxt->geom.heads || !cxt->geom.sectors
1517 				      || !cxt->geom.cylinders));
1518 
1519 	if (rc && !fdisk_is_listonly(cxt))
1520 		fdisk_warnx(cxt, _("Incomplete geometry setting."));
1521 
1522 	return rc;
1523 }
1524 
1525