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