blob: 2a4d377e05a43b76235284381c7fb238a79f8eac [file] [log] [blame]
#ifdef HAVE_LIBBLKID
# include <blkid.h>
#endif
#include "fdiskP.h"
/**
* SECTION: context
* @title: Context
* @short_description: stores info about device, labels etc.
*
* The library distinguish between three types of partitioning objects.
*
* on-disk data
* - disk label specific
* - probed and read by disklabel drivers when assign device to the context
* or when switch to another disk label type
* - only fdisk_write_disklabel() modify on-disk data
*
* in-memory data
* - generic data and disklabel specific data stored in struct fdisk_label
* - all partitioning operations are based on in-memory data only
*
* struct fdisk_partition
* - provides abstraction to present partitions to users
* - fdisk_partition is possible to gather to fdisk_table container
* - used as unified template for new partitions
* - the struct fdisk_partition is always completely independent object and
* any change to the object has no effect to in-memory (or on-disk) label data
*/
/**
* fdisk_new_context:
*
* Returns: newly allocated libfdisk handler
*/
struct fdisk_context *fdisk_new_context(void)
{
struct fdisk_context *cxt;
cxt = calloc(1, sizeof(*cxt));
if (!cxt)
return NULL;
DBG(CXT, ul_debugobj(cxt, "alloc"));
cxt->dev_fd = -1;
cxt->refcount = 1;
/*
* Allocate label specific structs.
*
* This is necessary (for example) to store label specific
* context setting.
*/
cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
return cxt;
}
static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
{
struct fdisk_context *parent;
assert(cxt);
assert(cxt->parent);
parent = cxt->parent;
cxt->alignment_offset = parent->alignment_offset;
cxt->ask_cb = parent->ask_cb;
cxt->ask_data = parent->ask_data;
cxt->dev_fd = parent->dev_fd;
cxt->first_lba = parent->first_lba;
cxt->firstsector_bufsz = parent->firstsector_bufsz;
cxt->firstsector = parent->firstsector;
cxt->geom = parent->geom;
cxt->grain = parent->grain;
cxt->io_size = parent->io_size;
cxt->last_lba = parent->last_lba;
cxt->min_io_size = parent->min_io_size;
cxt->optimal_io_size = parent->optimal_io_size;
cxt->phy_sector_size = parent->phy_sector_size;
cxt->readonly = parent->readonly;
cxt->script = parent->script;
fdisk_ref_script(cxt->script);
cxt->sector_size = parent->sector_size;
cxt->total_sectors = parent->total_sectors;
cxt->user_geom = parent->user_geom;
cxt->user_log_sector = parent->user_log_sector;
cxt->user_pyh_sector = parent->user_pyh_sector;
/* parent <--> nested independent setting, initialize for new nested
* contexts only */
if (isnew) {
cxt->listonly = parent->listonly;
cxt->display_details = parent->display_details;
cxt->display_in_cyl_units = parent->display_in_cyl_units;
}
free(cxt->dev_path);
cxt->dev_path = NULL;
if (parent->dev_path) {
cxt->dev_path = strdup(parent->dev_path);
if (!cxt->dev_path)
return -ENOMEM;
}
return 0;
}
/**
* fdisk_new_nested_context:
* @parent: parental context
* @name: optional label name (e.g. "bsd")
*
* Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
* The function also probes for the nested label on the device if device is
* already assigned to parent.
*
* The new context is initialized according to @parent and both context shares
* some settings and file descriptor to the device. The child propagate some
* changes (like fdisk_assign_device()) to parent, but it does not work
* vice-versa. The behavior is undefined if you assign another device to
* parent.
*
* Returns: new context for nested partition table.
*/
struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
const char *name)
{
struct fdisk_context *cxt;
struct fdisk_label *lb = NULL;
assert(parent);
cxt = calloc(1, sizeof(*cxt));
if (!cxt)
return NULL;
DBG(CXT, ul_debugobj(parent, "alloc nested [%p]", cxt));
cxt->refcount = 1;
fdisk_ref_context(parent);
cxt->parent = parent;
if (init_nested_from_parent(cxt, 1) != 0)
return NULL;
if (name) {
if (strcmp(name, "bsd") == 0)
lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
else if (strcmp(name, "dos") == 0)
lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
}
if (lb && parent->dev_fd >= 0) {
DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
cxt->label = lb;
if (lb->op->probe(cxt) == 1)
__fdisk_switch_label(cxt, lb);
else {
DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
if (lb->op->deinit)
lb->op->deinit(lb);
cxt->label = NULL;
}
}
return cxt;
}
/**
* fdisk_ref_context:
* @cxt: context pointer
*
* Increments reference counter.
*/
void fdisk_ref_context(struct fdisk_context *cxt)
{
if (cxt)
cxt->refcount++;
}
/**
* fdisk_get_label:
* @cxt: context instance
* @name: label name (e.g. "gpt")
*
* If no @name specified then returns the current context label.
*
* The label is allocated and maintained within the context #cxt. There is
* nothing like reference counting for labels, you cannot delallocate the
* label.
*
* Returns: label struct or NULL in case of error.
*/
struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
{
size_t i;
assert(cxt);
if (!name)
return cxt->label;
for (i = 0; i < cxt->nlabels; i++)
if (cxt->labels[i]
&& strcmp(cxt->labels[i]->name, name) == 0)
return cxt->labels[i];
DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
return NULL;
}
/**
* fdisk_next_label:
* @cxt: context instance
* @lb: returns pointer to the next label
*
* <informalexample>
* <programlisting>
* // print all supported labels
* struct fdisk_context *cxt = fdisk_new_context();
* struct fdisk_label *lb = NULL;
*
* while (fdisk_next_label(cxt, &lb) == 0)
* print("label name: %s\n", fdisk_label_get_name(lb));
* fdisk_unref_context(cxt);
* </programlisting>
* </informalexample>
*
* Returns: <0 in case of error, 0 on success, 1 at the end.
*/
int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
{
size_t i;
struct fdisk_label *res = NULL;
if (!lb || !cxt)
return -EINVAL;
if (!*lb)
res = cxt->labels[0];
else {
for (i = 1; i < cxt->nlabels; i++) {
if (*lb == cxt->labels[i - 1]) {
res = cxt->labels[i];
break;
}
}
}
*lb = res;
return res ? 0 : 1;
}
/**
* fdisk_get_nlabels:
* @cxt: context
*
* Returns: number of supported label types
*/
size_t fdisk_get_nlabels(struct fdisk_context *cxt)
{
return cxt ? cxt->nlabels : 0;
}
int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
{
if (!lb || !cxt)
return -EINVAL;
if (lb->disabled) {
DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
return -EINVAL;
}
cxt->label = lb;
DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
return 0;
}
/**
* fdisk_has_label:
* @cxt: fdisk context
*
* Returns: return 1 if there is label on the device.
*/
int fdisk_has_label(struct fdisk_context *cxt)
{
return cxt && cxt->label;
}
/**
* fdisk_get_npartitions:
* @cxt: context
*
* The maximal number of the partitions depends on disklabel and does not
* have to describe the real limit of PT.
*
* For example the limit for MBR without extend partition is 4, with extended
* partition it's unlimited (so the function returns the current number of all
* partitions in this case).
*
* And for example for GPT it depends on space allocated on disk for array of
* entry records (usually 128).
*
* It's fine to use fdisk_get_npartitions() in loops, but don't forget that
* partition may be unused (see fdisk_is_partition_used()).
*
* <informalexample>
* <programlisting>
* struct fdisk_partition *pa = NULL;
* size_t i, nmax = fdisk_get_npartitions(cxt);
*
* for (i = 0; i < nmax; i++) {
* if (!fdisk_is_partition_used(cxt, i))
* continue;
* ... do something ...
* }
* </programlisting>
* </informalexample>
*
* Note that the recommended way to list partitions is to use
* fdisk_get_partitions() and struct fdisk_table than ask disk driver for each
* individual partitions.
*
* Returns: maximal number of partitions for the current label.
*/
size_t fdisk_get_npartitions(struct fdisk_context *cxt)
{
return cxt && cxt->label ? cxt->label->nparts_max : 0;
}
/**
* fdisk_is_labeltype:
* @cxt: fdisk context
* @id: FDISK_DISKLABEL_*
*
* See also fdisk_is_label() macro in libfdisk.h.
*
* Returns: return 1 if the current label is @id
*/
int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
{
assert(cxt);
return cxt->label && fdisk_label_get_type(cxt->label) == id;
}
/**
* fdisk_get_parent:
* @cxt: nested fdisk context
*
* Returns: pointer to parental context, or NULL
*/
struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->parent;
}
static void reset_context(struct fdisk_context *cxt)
{
size_t i;
DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
/* reset drives' private data */
for (i = 0; i < cxt->nlabels; i++)
fdisk_deinit_label(cxt->labels[i]);
if (cxt->parent) {
/* the first sector may be independent on parent */
if (cxt->parent->firstsector != cxt->firstsector)
free(cxt->firstsector);
} else {
/* we close device only in primary context */
if (cxt->dev_fd > -1)
close(cxt->dev_fd);
free(cxt->firstsector);
}
free(cxt->dev_path);
cxt->dev_path = NULL;
cxt->dev_fd = -1;
cxt->firstsector = NULL;
cxt->firstsector_bufsz = 0;
fdisk_zeroize_device_properties(cxt);
fdisk_unref_script(cxt->script);
cxt->script = NULL;
cxt->label = NULL;
}
/*
* This function prints a warning if the device is not wiped (e.g. wipefs(8).
* Please don't call this function if there is already a PT.
*
* Returns: 0 if nothing found, < 0 on error, 1 if found a signature
*/
static int warn_wipe(struct fdisk_context *cxt)
{
#ifdef HAVE_LIBBLKID
blkid_probe pr;
#endif
int rc = 0;
assert(cxt);
if (fdisk_has_label(cxt) || cxt->dev_fd < 0)
return -EINVAL;
#ifdef HAVE_LIBBLKID
DBG(CXT, ul_debugobj(cxt, "wipe check: initialize libblkid prober"));
pr = blkid_new_probe();
if (!pr)
return -ENOMEM;
rc = blkid_probe_set_device(pr, cxt->dev_fd, 0, 0);
if (rc)
return rc;
blkid_probe_enable_superblocks(pr, 1);
blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
blkid_probe_enable_partitions(pr, 1);
/* we care about the first found FS/raid, so don't call blkid_do_probe()
* in loop or don't use blkid_do_fullprobe() ... */
rc = blkid_do_probe(pr);
if (rc == 0) {
const char *name = NULL;
if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0 ||
blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) {
fdisk_warnx(cxt, _(
"%s: device contains a valid '%s' signature; it is "
"strongly recommended to wipe the device with "
"wipefs(8) if this is unexpected, in order to "
"avoid possible collisions"), cxt->dev_path, name);
rc = 1;
}
}
blkid_free_probe(pr);
#endif
return rc;
}
/**
* fdisk_assign_device:
* @cxt: context
* @fname: path to the device to be handled
* @readonly: how to open the device
*
* Open the device, discovery topology, geometry, detect disklabel and switch
* the current label driver to reflect the probing result.
*
* Note that this function resets all generic setting in context. If the @cxt
* is nested context then the device is assigned to the parental context and
* necessary properties are copied to the @cxt. The change is propagated in
* child->parent direction only. It's impossible to use a different device for
* primary and nested contexts.
*
* Returns: 0 on success, < 0 on error.
*/
int fdisk_assign_device(struct fdisk_context *cxt,
const char *fname, int readonly)
{
int fd;
DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
assert(cxt);
/* redirect request to parent */
if (cxt->parent) {
int rc, org = fdisk_is_listonly(cxt->parent);
/* assign_device() is sensitive to "listonly" mode, so let's
* follow the current context setting for the parent to avoid
* unwanted extra warnings. */
fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
rc = fdisk_assign_device(cxt->parent, fname, readonly);
fdisk_enable_listonly(cxt->parent, org);
if (!rc)
rc = init_nested_from_parent(cxt, 0);
if (!rc)
fdisk_probe_labels(cxt);
return rc;
}
reset_context(cxt);
fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
if (fd < 0)
return -errno;
cxt->readonly = readonly;
cxt->dev_fd = fd;
cxt->dev_path = strdup(fname);
if (!cxt->dev_path)
goto fail;
fdisk_discover_topology(cxt);
fdisk_discover_geometry(cxt);
if (fdisk_read_firstsector(cxt) < 0)
goto fail;
/* detect labels and apply labes specific stuff (e.g geomery)
* to the context */
fdisk_probe_labels(cxt);
/* let's apply user geometry *after* label prober
* to make it possible to override in-label setting */
fdisk_apply_user_device_properties(cxt);
/* warn about obsolete stuff on the device if we aren't in
* list-only mode and there is not PT yet */
if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt))
warn_wipe(cxt);
DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
fname, readonly ? "READ-ONLY" : "READ-WRITE"));
return 0;
fail:
DBG(CXT, ul_debugobj(cxt, "failed to assign device"));
return -errno;
}
/**
* fdisk_deassign_device:
* @cxt: context
* @nosync: disable fsync()
*
* Close device and call fsync(). If the @cxt is nested context than the
* request is redirected to the parent.
*
* Returns: 0 on success, < 0 on error.
*/
int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
{
assert(cxt);
assert(cxt->dev_fd >= 0);
if (cxt->parent) {
int rc = fdisk_deassign_device(cxt->parent, nosync);
if (!rc)
rc = init_nested_from_parent(cxt, 0);
return rc;
}
if (cxt->readonly)
close(cxt->dev_fd);
else {
if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
fdisk_warn(cxt, _("%s: close device failed"),
cxt->dev_path);
return -errno;
}
if (!nosync) {
fdisk_info(cxt, _("Syncing disks."));
sync();
}
}
free(cxt->dev_path);
cxt->dev_path = NULL;
cxt->dev_fd = -1;
return 0;
}
/**
* fdisk_is_readonly:
* @cxt: context
*
* Returns: 1 if device open readonly
*/
int fdisk_is_readonly(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->readonly;
}
/**
* fdisk_unref_context:
* @cxt: fdisk context
*
* Deallocates context struct.
*/
void fdisk_unref_context(struct fdisk_context *cxt)
{
int i;
if (!cxt)
return;
cxt->refcount--;
if (cxt->refcount <= 0) {
DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
reset_context(cxt); /* this is sensitive to parent<->child relationship! */
/* deallocate label's private stuff */
for (i = 0; i < cxt->nlabels; i++) {
if (!cxt->labels[i])
continue;
if (cxt->labels[i]->op->free)
cxt->labels[i]->op->free(cxt->labels[i]);
else
free(cxt->labels[i]);
}
fdisk_unref_context(cxt->parent);
cxt->parent = NULL;
free(cxt);
}
}
/**
* fdisk_enable_details:
* @cxt: context
* @enable: true/flase
*
* Enables or disables "details" display mode. This function has effect to
* fdisk_partition_to_string() function.
*
* Returns: 0 on success, < 0 on error.
*/
int fdisk_enable_details(struct fdisk_context *cxt, int enable)
{
assert(cxt);
cxt->display_details = enable ? 1 : 0;
return 0;
}
/**
* fdisk_is_details:
* @cxt: context
*
* Returns: 1 if details are enabled
*/
int fdisk_is_details(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->display_details == 1;
}
/**
* fdisk_enable_listonly:
* @cxt: context
* @enable: true/flase
*
* Just list partition only, don't care about another details, mistakes, ...
*
* Returns: 0 on success, < 0 on error.
*/
int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
{
assert(cxt);
cxt->listonly = enable ? 1 : 0;
return 0;
}
/**
* fdisk_is_listonly:
* @cxt: context
*
* Returns: 1 if list-only mode enabled
*/
int fdisk_is_listonly(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->listonly == 1;
}
/**
* fdisk_set_unit:
* @cxt: context
* @str: "cylinder" or "sector".
*
* This is pure shit, unfortunately for example Sun addresses begin of the
* partition by cylinders...
*
* Returns: 0 on succes, <0 on error.
*/
int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
{
assert(cxt);
cxt->display_in_cyl_units = 0;
if (!str)
return 0;
if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
cxt->display_in_cyl_units = 1;
else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
cxt->display_in_cyl_units = 0;
DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
return 0;
}
/**
* fdisk_get_unit:
* @cxt: context
* @n: FDISK_PLURAL or FDISK_SINGULAR
*
* Returns: unit name.
*/
const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
{
assert(cxt);
if (fdisk_use_cylinders(cxt))
return P_("cylinder", "cylinders", n);
return P_("sector", "sectors", n);
}
/**
* fdisk_use_cylinders:
* @cxt: context
*
* Returns: 1 if user wants to display in cylinders.
*/
int fdisk_use_cylinders(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->display_in_cyl_units == 1;
}
/**
* fdisk_get_units_per_sector:
* @cxt: context
*
* This is necessary only for brain dead situations when we use "cylinders";
*
* Returns: number of "units" per sector, default is 1 if display unit is sector.
*/
unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
{
assert(cxt);
if (fdisk_use_cylinders(cxt)) {
assert(cxt->geom.heads);
return cxt->geom.heads * cxt->geom.sectors;
}
return 1;
}
/**
* fdisk_get_optimal_iosize:
* @cxt: context
*
* The optimal I/O is optional and does not have to be provided by device,
* anyway libfdisk never returns zero. If the optimal I/O size is not provided
* then libfdisk returns minimal I/O size or sector size.
*
* Returns: optimal I/O size in bytes.
*/
unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->optimal_io_size ? cxt->optimal_io_size : cxt->io_size;
}
/**
* fdisk_get_minimal_iosize:
* @cxt: context
*
* Returns: minimal I/O size in bytes
*/
unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->min_io_size;
}
/**
* fdisk_get_physector_size:
* @cxt: context
*
* Returns: physical sector size in bytes
*/
unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->phy_sector_size;
}
/**
* fdisk_get_sector_size:
* @cxt: context
*
* Returns: logical sector size in bytes
*/
unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->sector_size;
}
/**
* fdisk_get_alignment_offset
* @cxt: context
*
* The alignment offset is offset between logical and physical sectors. For
* backward compatibility the first logical sector on 4K disks does no have to
* start on the same place like physical sectors.
*
* Returns: alignment offset in bytes
*/
unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->alignment_offset;
}
/**
* fdisk_get_grain_size:
* @cxt: context
*
* Returns: grain in bytes used to align partitions (usually 1MiB)
*/
unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->grain;
}
/**
* fdisk_get_first_lba:
* @cxt: context
*
* Returns: first possible LBA on disk for data partitions.
*/
fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->first_lba;
}
/**
* fdisk_set_first_lba:
* @cxt: fdisk context
* @lba: first possible logical sector for data
*
* It's strongly recommended to use the default library setting. The first LBA
* is always reseted by fdisk_assign_device(), fdisk_override_geometry()
* and fdisk_reset_alignment(). This is very low level function and library
* does not check if your setting makes any sense.
*
* This function is necessary only when you want to work with very unusual
* partition tables like GPT protective MBR or hybrid partition tables on
* bootable media where the first partition may start on very crazy offsets.
*
* Returns: 0 on success, <0 on error.
*/
fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
{
assert(cxt);
DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
(uintmax_t) cxt->first_lba, (uintmax_t) lba));
cxt->first_lba = lba;
return 0;
}
/**
* fdisk_get_last_lba:
* @cxt: fdisk context
*
* Note that the device has to be already assigned.
*
* Returns: last possible LBA on device
*/
fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
{
return cxt->last_lba;
}
/**
* fdisk_set_last_lba:
* @cxt: fdisk context
* @lba: last possible logical sector
*
* It's strongly recommended to use the default library setting. The last LBA
* is always reseted by fdisk_assign_device(), fdisk_override_geometry() and
* fdisk_reset_alignment().
*
* The default is number of sectors on the device, but maybe modified by the
* current disklabel driver (for example GPT uses and of disk for backup
* header, so last_lba is smaller than total number of sectors).
*
* Returns: 0 on success, <0 on error.
*/
fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
{
assert(cxt);
if (lba > cxt->total_sectors - 1 && lba < 1)
return -ERANGE;
cxt->last_lba = lba;
return 0;
}
/**
* fdisk_get_nsectors:
* @cxt: context
*
* Returns: size of the device in logical sectors.
*/
fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->total_sectors;
}
/**
* fdisk_get_devname:
* @cxt: context
*
* Returns: device name.
*/
const char *fdisk_get_devname(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->dev_path;
}
/**
* fdisk_get_devfd:
* @cxt: context
*
* Retruns: device file descriptor.
*/
int fdisk_get_devfd(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->dev_fd;
}
/**
* fdisk_get_geom_heads:
* @cxt: context
*
* Returns: number of geometry heads.
*/
unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->geom.heads;
}
/**
* fdisk_get_geom_sectors:
* @cxt: context
*
* Returns: number of geometry sectors.
*/
fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->geom.sectors;
}
/**
* fdisk_get_geom_cylinders:
* @cxt: context
*
* Returns: number of geometry cylinders
*/
fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->geom.cylinders;
}
int fdisk_missing_geometry(struct fdisk_context *cxt)
{
int rc;
assert(cxt);
if (!cxt || !cxt->label)
return 0;
rc = (fdisk_label_require_geometry(cxt->label) &&
(!cxt->geom.heads || !cxt->geom.sectors
|| !cxt->geom.cylinders));
if (rc && !fdisk_is_listonly(cxt))
fdisk_warnx(cxt, _("Incomplete geometry setting."));
return rc;
}