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