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