]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/context.c
11 * @short_description: stores info about device, labels etc.
13 * The library distinguish between three types of partitioning objects.
16 * - disk label specific
17 * - probed and read by disklabel drivers when assign device to the context
18 * or when switch to another disk label type
19 * - only fdisk_write_disklabel() modify on-disk data
22 * - generic data and disklabel specific data stored in struct fdisk_label
23 * - all partitioning operations are based on in-memory data only
25 * struct fdisk_partition
26 * - provides abstraction to present partitions to users
27 * - fdisk_partition is possible to gather to fdisk_table container
28 * - used as unified template for new partitions
29 * - the struct fdisk_partition is always completely independent object and
30 * any change to the object has no effect to in-memory (or on-disk) label data
36 * Returns: newly allocated libfdisk handler
38 struct fdisk_context
*fdisk_new_context(void)
40 struct fdisk_context
*cxt
;
42 cxt
= calloc(1, sizeof(*cxt
));
46 DBG(CXT
, ul_debugobj(cxt
, "alloc"));
51 * Allocate label specific structs.
53 * This is necessary (for example) to store label specific
56 cxt
->labels
[ cxt
->nlabels
++ ] = fdisk_new_gpt_label(cxt
);
57 cxt
->labels
[ cxt
->nlabels
++ ] = fdisk_new_dos_label(cxt
);
58 cxt
->labels
[ cxt
->nlabels
++ ] = fdisk_new_bsd_label(cxt
);
59 cxt
->labels
[ cxt
->nlabels
++ ] = fdisk_new_sgi_label(cxt
);
60 cxt
->labels
[ cxt
->nlabels
++ ] = fdisk_new_sun_label(cxt
);
65 static int init_nested_from_parent(struct fdisk_context
*cxt
, int isnew
)
67 struct fdisk_context
*parent
;
74 cxt
->alignment_offset
= parent
->alignment_offset
;
75 cxt
->ask_cb
= parent
->ask_cb
;
76 cxt
->ask_data
= parent
->ask_data
;
77 cxt
->dev_fd
= parent
->dev_fd
;
78 cxt
->first_lba
= parent
->first_lba
;
79 cxt
->firstsector_bufsz
= parent
->firstsector_bufsz
;
80 cxt
->firstsector
= parent
->firstsector
;
81 cxt
->geom
= parent
->geom
;
82 cxt
->grain
= parent
->grain
;
83 cxt
->io_size
= parent
->io_size
;
84 cxt
->last_lba
= parent
->last_lba
;
85 cxt
->min_io_size
= parent
->min_io_size
;
86 cxt
->optimal_io_size
= parent
->optimal_io_size
;
87 cxt
->phy_sector_size
= parent
->phy_sector_size
;
88 cxt
->readonly
= parent
->readonly
;
89 cxt
->script
= parent
->script
;
90 fdisk_ref_script(cxt
->script
);
91 cxt
->sector_size
= parent
->sector_size
;
92 cxt
->total_sectors
= parent
->total_sectors
;
93 cxt
->user_geom
= parent
->user_geom
;
94 cxt
->user_log_sector
= parent
->user_log_sector
;
95 cxt
->user_pyh_sector
= parent
->user_pyh_sector
;
97 /* parent <--> nested independent setting, initialize for new nested
100 cxt
->listonly
= parent
->listonly
;
101 cxt
->display_details
= parent
->display_details
;
102 cxt
->display_in_cyl_units
= parent
->display_in_cyl_units
;
106 cxt
->dev_path
= NULL
;
108 if (parent
->dev_path
) {
109 cxt
->dev_path
= strdup(parent
->dev_path
);
118 * fdisk_new_nested_context:
119 * @parent: parental context
120 * @name: optional label name (e.g. "bsd")
122 * Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
123 * The function also probes for the nested label on the device if device is
124 * already assigned to parent.
126 * The new context is initialized according to @parent and both context shares
127 * some settings and file descriptor to the device. The child propagate some
128 * changes (like fdisk_assign_device()) to parent, but it does not work
129 * vice-versa. The behavior is undefined if you assign another device to
132 * Returns: new context for nested partition table.
134 struct fdisk_context
*fdisk_new_nested_context(struct fdisk_context
*parent
,
137 struct fdisk_context
*cxt
;
138 struct fdisk_label
*lb
= NULL
;
142 cxt
= calloc(1, sizeof(*cxt
));
146 DBG(CXT
, ul_debugobj(parent
, "alloc nested [%p]", cxt
));
149 fdisk_ref_context(parent
);
150 cxt
->parent
= parent
;
152 if (init_nested_from_parent(cxt
, 1) != 0)
156 if (strcmp(name
, "bsd") == 0)
157 lb
= cxt
->labels
[ cxt
->nlabels
++ ] = fdisk_new_bsd_label(cxt
);
158 else if (strcmp(name
, "dos") == 0)
159 lb
= cxt
->labels
[ cxt
->nlabels
++ ] = fdisk_new_dos_label(cxt
);
162 if (lb
&& parent
->dev_fd
>= 0) {
163 DBG(CXT
, ul_debugobj(cxt
, "probing for nested %s", lb
->name
));
167 if (lb
->op
->probe(cxt
) == 1)
168 __fdisk_switch_label(cxt
, lb
);
170 DBG(CXT
, ul_debugobj(cxt
, "not found %s label", lb
->name
));
183 * @cxt: context pointer
185 * Increments reference counter.
187 void fdisk_ref_context(struct fdisk_context
*cxt
)
195 * @cxt: context instance
196 * @name: label name (e.g. "gpt")
198 * If no @name specified then returns the current context label.
200 * The label is allocated and maintained within the context #cxt. There is
201 * nothing like reference counting for labels, you cannot delallocate the
204 * Returns: label struct or NULL in case of error.
206 struct fdisk_label
*fdisk_get_label(struct fdisk_context
*cxt
, const char *name
)
215 for (i
= 0; i
< cxt
->nlabels
; i
++)
217 && strcmp(cxt
->labels
[i
]->name
, name
) == 0)
218 return cxt
->labels
[i
];
220 DBG(CXT
, ul_debugobj(cxt
, "failed to found %s label driver", name
));
226 * @cxt: context instance
227 * @lb: returns pointer to the next label
231 * // print all supported labels
232 * struct fdisk_context *cxt = fdisk_new_context();
233 * struct fdisk_label *lb = NULL;
235 * while (fdisk_next_label(cxt, &lb) == 0)
236 * print("label name: %s\n", fdisk_label_get_name(lb));
237 * fdisk_unref_context(cxt);
241 * Returns: <0 in case of error, 0 on success, 1 at the end.
243 int fdisk_next_label(struct fdisk_context
*cxt
, struct fdisk_label
**lb
)
246 struct fdisk_label
*res
= NULL
;
252 res
= cxt
->labels
[0];
254 for (i
= 1; i
< cxt
->nlabels
; i
++) {
255 if (*lb
== cxt
->labels
[i
- 1]) {
256 res
= cxt
->labels
[i
];
270 * Returns: number of supported label types
272 size_t fdisk_get_nlabels(struct fdisk_context
*cxt
)
274 return cxt
? cxt
->nlabels
: 0;
277 int __fdisk_switch_label(struct fdisk_context
*cxt
, struct fdisk_label
*lb
)
282 DBG(CXT
, ul_debugobj(cxt
, "*** attempt to switch to disabled label %s -- ignore!", lb
->name
));
286 DBG(CXT
, ul_debugobj(cxt
, "--> switching context to %s!", lb
->name
));
292 * @cxt: fdisk context
294 * Returns: return 1 if there is label on the device.
296 int fdisk_has_label(struct fdisk_context
*cxt
)
298 return cxt
&& cxt
->label
;
302 * fdisk_get_npartitions:
305 * The maximal number of the partitions depends on disklabel and does not
306 * have to describe the real limit of PT.
308 * For example the limit for MBR without extend partition is 4, with extended
309 * partition it's unlimited (so the function returns the current number of all
310 * partitions in this case).
312 * And for example for GPT it depends on space allocated on disk for array of
313 * entry records (usually 128).
315 * It's fine to use fdisk_get_npartitions() in loops, but don't forget that
316 * partition may be unused (see fdisk_is_partition_used()).
320 * struct fdisk_partition *pa = NULL;
321 * size_t i, nmax = fdisk_get_npartitions(cxt);
323 * for (i = 0; i < nmax; i++) {
324 * if (!fdisk_is_partition_used(cxt, i))
326 * ... do something ...
331 * Note that the recommended way to list partitions is to use
332 * fdisk_get_partitions() and struct fdisk_table than ask disk driver for each
333 * individual partitions.
335 * Returns: maximal number of partitions for the current label.
337 size_t fdisk_get_npartitions(struct fdisk_context
*cxt
)
339 return cxt
&& cxt
->label
? cxt
->label
->nparts_max
: 0;
343 * fdisk_is_labeltype:
344 * @cxt: fdisk context
345 * @id: FDISK_DISKLABEL_*
347 * See also fdisk_is_label() macro in libfdisk.h.
349 * Returns: return 1 if the current label is @id
351 int fdisk_is_labeltype(struct fdisk_context
*cxt
, enum fdisk_labeltype id
)
355 return cxt
->label
&& fdisk_label_get_type(cxt
->label
) == id
;
360 * @cxt: nested fdisk context
362 * Returns: pointer to parental context, or NULL
364 struct fdisk_context
*fdisk_get_parent(struct fdisk_context
*cxt
)
370 static void reset_context(struct fdisk_context
*cxt
)
374 DBG(CXT
, ul_debugobj(cxt
, "*** resetting context"));
376 /* reset drives' private data */
377 for (i
= 0; i
< cxt
->nlabels
; i
++)
378 fdisk_deinit_label(cxt
->labels
[i
]);
381 /* the first sector may be independent on parent */
382 if (cxt
->parent
->firstsector
!= cxt
->firstsector
)
383 free(cxt
->firstsector
);
385 /* we close device only in primary context */
386 if (cxt
->dev_fd
> -1)
388 free(cxt
->firstsector
);
392 cxt
->dev_path
= NULL
;
395 cxt
->firstsector
= NULL
;
396 cxt
->firstsector_bufsz
= 0;
398 fdisk_zeroize_device_properties(cxt
);
400 fdisk_unref_script(cxt
->script
);
407 * This function prints a warning if the device is not wiped (e.g. wipefs(8).
408 * Please don't call this function if there is already a PT.
410 * Returns: 0 if nothing found, < 0 on error, 1 if found a signature
412 static int warn_wipe(struct fdisk_context
*cxt
)
421 if (fdisk_has_label(cxt
) || cxt
->dev_fd
< 0)
424 DBG(CXT
, ul_debugobj(cxt
, "wipe check: initialize libblkid prober"));
426 pr
= blkid_new_probe();
429 rc
= blkid_probe_set_device(pr
, cxt
->dev_fd
, 0, 0);
433 blkid_probe_enable_superblocks(pr
, 1);
434 blkid_probe_set_superblocks_flags(pr
, BLKID_SUBLKS_TYPE
);
435 blkid_probe_enable_partitions(pr
, 1);
437 /* we care about the first found FS/raid, so don't call blkid_do_probe()
438 * in loop or don't use blkid_do_fullprobe() ... */
439 rc
= blkid_do_probe(pr
);
441 const char *name
= NULL
;
443 if (blkid_probe_lookup_value(pr
, "TYPE", &name
, 0) == 0 ||
444 blkid_probe_lookup_value(pr
, "PTTYPE", &name
, 0) == 0) {
446 "%s: device contains a valid '%s' signature; it is "
447 "strongly recommended to wipe the device with "
448 "wipefs(8) if this is unexpected, in order to "
449 "avoid possible collisions"), cxt
->dev_path
, name
);
454 blkid_free_probe(pr
);
460 * fdisk_assign_device:
462 * @fname: path to the device to be handled
463 * @readonly: how to open the device
465 * Open the device, discovery topology, geometry, detect disklabel and switch
466 * the current label driver to reflect the probing result.
468 * Note that this function resets all generic setting in context. If the @cxt
469 * is nested context then the device is assigned to the parental context and
470 * necessary properties are copied to the @cxt. The change is propagated in
471 * child->parent direction only. It's impossible to use a different device for
472 * primary and nested contexts.
474 * Returns: 0 on success, < 0 on error.
476 int fdisk_assign_device(struct fdisk_context
*cxt
,
477 const char *fname
, int readonly
)
481 DBG(CXT
, ul_debugobj(cxt
, "assigning device %s", fname
));
484 /* redirect request to parent */
486 int rc
, org
= fdisk_is_listonly(cxt
->parent
);
488 /* assign_device() is sensitive to "listonly" mode, so let's
489 * follow the current context setting for the parent to avoid
490 * unwanted extra warnings. */
491 fdisk_enable_listonly(cxt
->parent
, fdisk_is_listonly(cxt
));
493 rc
= fdisk_assign_device(cxt
->parent
, fname
, readonly
);
494 fdisk_enable_listonly(cxt
->parent
, org
);
497 rc
= init_nested_from_parent(cxt
, 0);
499 fdisk_probe_labels(cxt
);
505 fd
= open(fname
, (readonly
? O_RDONLY
: O_RDWR
) | O_CLOEXEC
);
509 cxt
->readonly
= readonly
;
511 cxt
->dev_path
= strdup(fname
);
515 fdisk_discover_topology(cxt
);
516 fdisk_discover_geometry(cxt
);
518 if (fdisk_read_firstsector(cxt
) < 0)
521 /* detect labels and apply labes specific stuff (e.g geomery)
523 fdisk_probe_labels(cxt
);
525 /* let's apply user geometry *after* label prober
526 * to make it possible to override in-label setting */
527 fdisk_apply_user_device_properties(cxt
);
529 /* warn about obsolete stuff on the device if we aren't in
530 * list-only mode and there is not PT yet */
531 if (!fdisk_is_listonly(cxt
) && !fdisk_has_label(cxt
))
534 DBG(CXT
, ul_debugobj(cxt
, "initialized for %s [%s]",
535 fname
, readonly
? "READ-ONLY" : "READ-WRITE"));
538 DBG(CXT
, ul_debugobj(cxt
, "failed to assign device"));
543 * fdisk_deassign_device:
545 * @nosync: disable fsync()
547 * Close device and call fsync(). If the @cxt is nested context than the
548 * request is redirected to the parent.
550 * Returns: 0 on success, < 0 on error.
552 int fdisk_deassign_device(struct fdisk_context
*cxt
, int nosync
)
555 assert(cxt
->dev_fd
>= 0);
558 int rc
= fdisk_deassign_device(cxt
->parent
, nosync
);
561 rc
= init_nested_from_parent(cxt
, 0);
568 if (fsync(cxt
->dev_fd
) || close(cxt
->dev_fd
)) {
569 fdisk_warn(cxt
, _("%s: close device failed"),
575 fdisk_info(cxt
, _("Syncing disks."));
581 cxt
->dev_path
= NULL
;
592 * Returns: 1 if device open readonly
594 int fdisk_is_readonly(struct fdisk_context
*cxt
)
597 return cxt
->readonly
;
601 * fdisk_unref_context:
602 * @cxt: fdisk context
604 * Deallocates context struct.
606 void fdisk_unref_context(struct fdisk_context
*cxt
)
614 if (cxt
->refcount
<= 0) {
615 DBG(CXT
, ul_debugobj(cxt
, "freeing context %p for %s", cxt
, cxt
->dev_path
));
617 reset_context(cxt
); /* this is sensitive to parent<->child relationship! */
619 /* deallocate label's private stuff */
620 for (i
= 0; i
< cxt
->nlabels
; i
++) {
623 if (cxt
->labels
[i
]->op
->free
)
624 cxt
->labels
[i
]->op
->free(cxt
->labels
[i
]);
626 free(cxt
->labels
[i
]);
629 fdisk_unref_context(cxt
->parent
);
638 * fdisk_enable_details:
640 * @enable: true/flase
642 * Enables or disables "details" display mode. This function has effect to
643 * fdisk_partition_to_string() function.
645 * Returns: 0 on success, < 0 on error.
647 int fdisk_enable_details(struct fdisk_context
*cxt
, int enable
)
650 cxt
->display_details
= enable
? 1 : 0;
658 * Returns: 1 if details are enabled
660 int fdisk_is_details(struct fdisk_context
*cxt
)
663 return cxt
->display_details
== 1;
667 * fdisk_enable_listonly:
669 * @enable: true/flase
671 * Just list partition only, don't care about another details, mistakes, ...
673 * Returns: 0 on success, < 0 on error.
675 int fdisk_enable_listonly(struct fdisk_context
*cxt
, int enable
)
678 cxt
->listonly
= enable
? 1 : 0;
686 * Returns: 1 if list-only mode enabled
688 int fdisk_is_listonly(struct fdisk_context
*cxt
)
691 return cxt
->listonly
== 1;
698 * @str: "cylinder" or "sector".
700 * This is pure shit, unfortunately for example Sun addresses begin of the
701 * partition by cylinders...
703 * Returns: 0 on succes, <0 on error.
705 int fdisk_set_unit(struct fdisk_context
*cxt
, const char *str
)
709 cxt
->display_in_cyl_units
= 0;
714 if (strcmp(str
, "cylinder") == 0 || strcmp(str
, "cylinders") == 0)
715 cxt
->display_in_cyl_units
= 1;
717 else if (strcmp(str
, "sector") == 0 || strcmp(str
, "sectors") == 0)
718 cxt
->display_in_cyl_units
= 0;
720 DBG(CXT
, ul_debugobj(cxt
, "display unit: %s", fdisk_get_unit(cxt
, 0)));
727 * @n: FDISK_PLURAL or FDISK_SINGULAR
729 * Returns: unit name.
731 const char *fdisk_get_unit(struct fdisk_context
*cxt
, int n
)
735 if (fdisk_use_cylinders(cxt
))
736 return P_("cylinder", "cylinders", n
);
737 return P_("sector", "sectors", n
);
741 * fdisk_use_cylinders:
744 * Returns: 1 if user wants to display in cylinders.
746 int fdisk_use_cylinders(struct fdisk_context
*cxt
)
749 return cxt
->display_in_cyl_units
== 1;
753 * fdisk_get_units_per_sector:
756 * This is necessary only for brain dead situations when we use "cylinders";
758 * Returns: number of "units" per sector, default is 1 if display unit is sector.
760 unsigned int fdisk_get_units_per_sector(struct fdisk_context
*cxt
)
764 if (fdisk_use_cylinders(cxt
)) {
765 assert(cxt
->geom
.heads
);
766 return cxt
->geom
.heads
* cxt
->geom
.sectors
;
772 * fdisk_get_optimal_iosize:
775 * The optimal I/O is optional and does not have to be provided by device,
776 * anyway libfdisk never returns zero. If the optimal I/O size is not provided
777 * then libfdisk returns minimal I/O size or sector size.
779 * Returns: optimal I/O size in bytes.
781 unsigned long fdisk_get_optimal_iosize(struct fdisk_context
*cxt
)
784 return cxt
->optimal_io_size
? cxt
->optimal_io_size
: cxt
->io_size
;
788 * fdisk_get_minimal_iosize:
791 * Returns: minimal I/O size in bytes
793 unsigned long fdisk_get_minimal_iosize(struct fdisk_context
*cxt
)
796 return cxt
->min_io_size
;
800 * fdisk_get_physector_size:
803 * Returns: physical sector size in bytes
805 unsigned long fdisk_get_physector_size(struct fdisk_context
*cxt
)
808 return cxt
->phy_sector_size
;
812 * fdisk_get_sector_size:
815 * Returns: logical sector size in bytes
817 unsigned long fdisk_get_sector_size(struct fdisk_context
*cxt
)
820 return cxt
->sector_size
;
824 * fdisk_get_alignment_offset
827 * The alignment offset is offset between logical and physical sectors. For
828 * backward compatibility the first logical sector on 4K disks does no have to
829 * start on the same place like physical sectors.
831 * Returns: alignment offset in bytes
833 unsigned long fdisk_get_alignment_offset(struct fdisk_context
*cxt
)
836 return cxt
->alignment_offset
;
840 * fdisk_get_grain_size:
843 * Returns: grain in bytes used to align partitions (usually 1MiB)
845 unsigned long fdisk_get_grain_size(struct fdisk_context
*cxt
)
852 * fdisk_get_first_lba:
855 * Returns: first possible LBA on disk for data partitions.
857 fdisk_sector_t
fdisk_get_first_lba(struct fdisk_context
*cxt
)
860 return cxt
->first_lba
;
864 * fdisk_set_first_lba:
865 * @cxt: fdisk context
866 * @lba: first possible logical sector for data
868 * It's strongly recommended to use the default library setting. The first LBA
869 * is always reseted by fdisk_assign_device(), fdisk_override_geometry()
870 * and fdisk_reset_alignment(). This is very low level function and library
871 * does not check if your setting makes any sense.
873 * This function is necessary only when you want to work with very unusual
874 * partition tables like GPT protective MBR or hybrid partition tables on
875 * bootable media where the first partition may start on very crazy offsets.
877 * Returns: 0 on success, <0 on error.
879 fdisk_sector_t
fdisk_set_first_lba(struct fdisk_context
*cxt
, fdisk_sector_t lba
)
882 DBG(CXT
, ul_debugobj(cxt
, "setting first LBA from %ju to %ju",
883 (uintmax_t) cxt
->first_lba
, (uintmax_t) lba
));
884 cxt
->first_lba
= lba
;
889 * fdisk_get_last_lba:
890 * @cxt: fdisk context
892 * Note that the device has to be already assigned.
894 * Returns: last possible LBA on device
896 fdisk_sector_t
fdisk_get_last_lba(struct fdisk_context
*cxt
)
898 return cxt
->last_lba
;
902 * fdisk_set_last_lba:
903 * @cxt: fdisk context
904 * @lba: last possible logical sector
906 * It's strongly recommended to use the default library setting. The last LBA
907 * is always reseted by fdisk_assign_device(), fdisk_override_geometry() and
908 * fdisk_reset_alignment().
910 * The default is number of sectors on the device, but maybe modified by the
911 * current disklabel driver (for example GPT uses the end of disk for backup
912 * header, so last_lba is smaller than total number of sectors).
914 * Returns: 0 on success, <0 on error.
916 fdisk_sector_t
fdisk_set_last_lba(struct fdisk_context
*cxt
, fdisk_sector_t lba
)
920 if (lba
> cxt
->total_sectors
- 1 && lba
< 1)
928 * fdisk_get_nsectors:
931 * Returns: size of the device in logical sectors.
933 fdisk_sector_t
fdisk_get_nsectors(struct fdisk_context
*cxt
)
936 return cxt
->total_sectors
;
943 * Returns: device name.
945 const char *fdisk_get_devname(struct fdisk_context
*cxt
)
948 return cxt
->dev_path
;
955 * Retruns: device file descriptor.
957 int fdisk_get_devfd(struct fdisk_context
*cxt
)
964 * fdisk_get_geom_heads:
967 * Returns: number of geometry heads.
969 unsigned int fdisk_get_geom_heads(struct fdisk_context
*cxt
)
972 return cxt
->geom
.heads
;
975 * fdisk_get_geom_sectors:
978 * Returns: number of geometry sectors.
980 fdisk_sector_t
fdisk_get_geom_sectors(struct fdisk_context
*cxt
)
983 return cxt
->geom
.sectors
;
988 * fdisk_get_geom_cylinders:
991 * Returns: number of geometry cylinders
993 fdisk_sector_t
fdisk_get_geom_cylinders(struct fdisk_context
*cxt
)
996 return cxt
->geom
.cylinders
;
999 int fdisk_missing_geometry(struct fdisk_context
*cxt
)
1005 if (!cxt
|| !cxt
->label
)
1008 rc
= (fdisk_label_require_geometry(cxt
->label
) &&
1009 (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
1010 || !cxt
->geom
.cylinders
));
1012 if (rc
&& !fdisk_is_listonly(cxt
))
1013 fdisk_warnx(cxt
, _("Incomplete geometry setting."));