From b13154de3eca5ba28fbb4854d916cd0be5febeed Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Fri, 2 Nov 2012 20:15:39 +0400 Subject: Imported Upstream version 2.22 --- fdisks/utils.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 523 insertions(+) create mode 100644 fdisks/utils.c (limited to 'fdisks/utils.c') diff --git a/fdisks/utils.c b/fdisks/utils.c new file mode 100644 index 0000000..85e09c5 --- /dev/null +++ b/fdisks/utils.c @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2012 Davidlohr Bueso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#ifdef HAVE_LIBBLKID +#include +#endif + +#include "nls.h" +#include "blkdev.h" +#include "common.h" +#include "fdisk.h" + +#include "fdiskdoslabel.h" +#include "fdisksunlabel.h" + +int fdisk_debug_mask; + +/* + * Label probing functions. + */ +static const struct fdisk_label *labels[] = +{ + &dos_label, + &sun_label, + &sgi_label, + &aix_label, + &bsd_label, + &mac_label, +}; + +/** + * fdisk_write_disklabel + * @cxt: fdisk context + * + * Write in-memory changes to disk + * + * Returns 0 on success, otherwise, a corresponding error. + */ +int fdisk_write_disklabel(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->write) + return -ENOSYS; + + return cxt->label->write(cxt); +} + +/** + * fdisk_verify_disklabel: + * @cxt: fdisk context + * + * Verifies the partition tabe. + * + * Returns 0. + */ +int fdisk_verify_disklabel(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->verify) + return -ENOSYS; + + return cxt->label->verify(cxt); +} + +/** + * fdisk_add_partition: + * @cxt: fdisk context + * @partnum: partition number to create + * @parttype: partition type to create + * + * Creates a new partition, with number @partnum and type @parttype. + * + * Returns 0. + */ +int fdisk_add_partition(struct fdisk_context *cxt, int partnum, int parttype) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->part_add) + return -ENOSYS; + + cxt->label->part_add(cxt, partnum, parttype); + return 0; +} + +/** + * fdisk_delete_partition: + * @cxt: fdisk context + * @partnum: partition number to delete + * + * Deletes a @partnum partition. + * + * Returns 0 on success, otherwise, a corresponding error. + */ +int fdisk_delete_partition(struct fdisk_context *cxt, int partnum) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->part_delete) + return -ENOSYS; + + DBG(LABEL, dbgprint("deleting %s partition number %d", + cxt->label->name, partnum)); + cxt->label->part_delete(cxt, partnum); + return 0; +} + +static int __probe_labels(struct fdisk_context *cxt) +{ + size_t i; + + disklabel = ANY_LABEL; + update_units(cxt); + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (!labels[i]->probe || labels[i]->probe(cxt) != 1) + continue; + + cxt->label = labels[i]; + + DBG(LABEL, dbgprint("detected a %s label", cxt->label->name)); + return 0; + } + + return 1; /* not found */ +} + +static int __init_firstsector_buffer(struct fdisk_context *cxt) +{ + DBG(TOPOLOGY, dbgprint("initialize first sector buffer")); + + cxt->firstsector = calloc(1, MAX_SECTOR_SIZE); + if (!cxt->firstsector) + goto fail; + + /* read MBR */ + if (512 != read(cxt->dev_fd, cxt->firstsector, 512)) { + if (errno == 0) + errno = EINVAL; /* probably too small file/device */ + goto fail; + } + + return 0; +fail: + return -errno; +} + +static unsigned long __get_sector_size(int fd) +{ + int sect_sz; + + if (!blkdev_get_sector_size(fd, §_sz)) + return (unsigned long) sect_sz; + return DEFAULT_SECTOR_SIZE; +} + +/** + * fdisk_context_force_sector_size: + * @cxt: fdisk context + * @s: required sector size + * + * Overwrites logical and physical sector size. Note that the default sector + * size is discovered by fdisk_new_context_from_device() from device topology. + * + * Don't use this function, rely on the default behavioer is more safe. + * + * Returns: 0 on success, < 0 on error. + */ +int fdisk_context_force_sector_size(struct fdisk_context *cxt, sector_t s) +{ + if (!cxt) + return -EINVAL; + + cxt->phy_sector_size = cxt->sector_size = s; + cxt->min_io_size = cxt->io_size = s; + + update_sector_offset(cxt); + return 0; +} + +static void recount_geometry(struct fdisk_context *cxt) +{ + cxt->geom.cylinders = cxt->total_sectors / + (cxt->geom.heads * cxt->geom.sectors); +} + +/** + * fdisk_context_set_user_geometry: + * @cxt: fdisk context + * @cylinders: user specified cylinders + * @heads: user specified heads + * @sectors: user specified sectors + * + * Overrides autodiscovery and apply user specified geometry. + * + * Returns: 0 on success, < 0 on error. + */ +int fdisk_context_set_user_geometry(struct fdisk_context *cxt, + unsigned int cylinders, + unsigned int heads, + unsigned int sectors) +{ + if (!cxt) + return -EINVAL; + if (heads) + cxt->geom.heads = heads; + if (sectors) + cxt->geom.sectors = sectors; + + if (cylinders) + cxt->geom.cylinders = cylinders; + else + recount_geometry(cxt); + + update_sector_offset(cxt); + return 0; +} + +/* + * Generic (label independent) geometry + */ +static int __discover_system_geometry(struct fdisk_context *cxt) +{ + sector_t nsects; + unsigned int h = 0, s = 0; + + /* get number of 512-byte sectors, and convert it the real sectors */ + if (!blkdev_get_sectors(cxt->dev_fd, &nsects)) + cxt->total_sectors = (nsects / (cxt->sector_size >> 9)); + + /* what the kernel/bios thinks the geometry is */ + blkdev_get_geometry(cxt->dev_fd, &h, &s); + if (!h && !s) { + /* unable to discover geometry, use default values */ + s = 63; + h = 255; + } + + /* obtained heads and sectors */ + cxt->geom.heads = h; + cxt->geom.sectors = s; + recount_geometry(cxt); + + DBG(GEOMETRY, dbgprint("geometry discovered for %s: C/H/S: %lld/%d/%lld", + cxt->dev_path, cxt->geom.cylinders, + cxt->geom.heads, cxt->geom.sectors)); + return 0; +} + +static int __discover_topology(struct fdisk_context *cxt) +{ +#ifdef HAVE_LIBBLKID + blkid_probe pr; + + DBG(TOPOLOGY, dbgprint("initialize libblkid prober")); + + pr = blkid_new_probe(); + if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) { + blkid_topology tp = blkid_probe_get_topology(pr); + + if (tp) { + cxt->min_io_size = blkid_topology_get_minimum_io_size(tp); + cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp); + cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp); + cxt->alignment_offset = blkid_topology_get_alignment_offset(tp); + + /* I/O size used by fdisk */ + cxt->io_size = cxt->optimal_io_size; + if (!cxt->io_size) + /* optimal IO is optional, default to minimum IO */ + cxt->io_size = cxt->min_io_size; + } + } + blkid_free_probe(pr); +#endif + + /* no blkid or error, use default values */ + if (!cxt->min_io_size) + cxt->min_io_size = DEFAULT_SECTOR_SIZE; + if (!cxt->io_size) + cxt->io_size = DEFAULT_SECTOR_SIZE; + + cxt->sector_size = __get_sector_size(cxt->dev_fd); + if (!cxt->phy_sector_size) /* could not discover physical size */ + cxt->phy_sector_size = cxt->sector_size; + + DBG(TOPOLOGY, dbgprint("topology discovered for %s:\n" + "\tlogical/physical sector sizes: %ld/%ld\n" + "\tfdisk/minimal/optimal io sizes: %ld/%ld/%ld\n", + cxt->dev_path, cxt->sector_size, cxt->phy_sector_size, + cxt->io_size, cxt->optimal_io_size, cxt->min_io_size)); + return 0; +} + +/** + * fdisk_zeroize_firstsector: + * @cxt: fdisk context + * + * Zeros in-memory first sector buffer + */ +void fdisk_zeroize_firstsector(struct fdisk_context *cxt) +{ + if (!cxt) + return; + + if (cxt->firstsector) { + DBG(CONTEXT, dbgprint("zeroize in-memory first sector buffer")); + memset(cxt->firstsector, 0, MAX_SECTOR_SIZE); + } +} + +/** + * fdisk_dev_sectsz_is_default: + * @cxt: fdisk context + * + * Returns 1 if the device's sector size is the default value, otherwise 0. + */ +int fdisk_dev_sectsz_is_default(struct fdisk_context *cxt) +{ + if (!cxt) + return -EINVAL; + + return cxt->sector_size == DEFAULT_SECTOR_SIZE; +} + +/** + * fdisk_dev_has_topology: + * @cxt: fdisk context + * + * Returns 1 if the device provides topology information, otherwise 0. + */ +int fdisk_dev_has_topology(struct fdisk_context *cxt) +{ + /* + * Assume that the device provides topology info if + * optimal_io_size is set or alignment_offset is set or + * minimum_io_size is not power of 2. + */ + if (cxt && + (cxt->optimal_io_size || + cxt->alignment_offset || + !is_power_of_2(cxt->min_io_size))) + return 1; + return 0; +} + +/** + * fdisk_dev_has_disklabel: + * @cxt: fdisk context + * + * Returns: return 1 if there is label on the device. + */ +int fdisk_dev_has_disklabel(struct fdisk_context *cxt) +{ + return cxt && disklabel != ANY_LABEL; +} + +/** + * fdisk_create_disklabel: + * @cxt: fdisk context + * @name: label name + * + * Creates a new disk label of type @name. If @name is NULL, then it + * will create a default system label type, either SUN or DOS. + * + * Returns 0 on success, otherwise, a corresponding error. + */ +int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name) +{ + if (!cxt) + return -EINVAL; + + cxt->label = NULL; + + if (!name) { /* use default label creation */ +#ifdef __sparc__ + cxt->label = &sun_label; +#else + cxt->label = &dos_label; +#endif + } else { + size_t i; + + for (i = 0; i < ARRAY_SIZE(labels); i++) { + if (strcmp(name, labels[i]->name) != 0) + continue; + + cxt->label = labels[i]; + DBG(LABEL, dbgprint("changing to %s label\n", cxt->label->name)); + break; + } + } + + if (!cxt->label) + return -EINVAL; + if (!cxt->label->create) + return -ENOSYS; + + return cxt->label->create(cxt); +} + +/** + * fdisk_init_debug: + * @mask: debug mask (0xffff to enable full debuging) + * + * If the @mask is not specified then this function reads + * FDISK_DEBUG environment variable to get the mask. + * + * Already initialized debugging stuff cannot be changed. It does not + * have effect to call this function twice. + */ +void fdisk_init_debug(int mask) +{ + if (fdisk_debug_mask & FDISK_DEBUG_INIT) + return; + if (!mask) { + char *str = getenv("FDISK_DEBUG"); + if (str) + fdisk_debug_mask = strtoul(str, 0, 0); + } else + fdisk_debug_mask = mask; + + if (fdisk_debug_mask) + fprintf(stderr, "fdisk: debug mask set to 0x%04x.\n", + fdisk_debug_mask); + fdisk_debug_mask |= FDISK_DEBUG_INIT; +} + +/** + * fdisk_new_context: + * @filename: path to the device to be handled + * @readonly: how to open the device + * + * If the @readonly flag is set to false, fdisk will attempt to open + * the device with read-write mode and will fallback to read-only if + * unsuccessful. + * + * Returns: newly allocated fdisk context + */ +struct fdisk_context *fdisk_new_context_from_filename(const char *fname, int readonly) +{ + int fd, errsv = 0; + struct fdisk_context *cxt = NULL; + + DBG(CONTEXT, dbgprint("initializing context for %s", fname)); + + if (readonly == 1 || (fd = open(fname, O_RDWR)) < 0) { + if ((fd = open(fname, O_RDONLY)) < 0) + return NULL; + readonly = 1; + } + + cxt = calloc(1, sizeof(*cxt)); + if (!cxt) + goto fail; + + cxt->dev_fd = fd; + cxt->dev_path = strdup(fname); + if (!cxt->dev_path) + goto fail; + + if (__init_firstsector_buffer(cxt) < 0) + goto fail; + + __discover_topology(cxt); + __discover_system_geometry(cxt); + + /* detect labels and apply labes specific stuff (e.g geomery) + * to the context */ + __probe_labels(cxt); + + update_sector_offset(cxt); + + DBG(CONTEXT, dbgprint("context initialized for %s [%s]", + fname, readonly ? "READ-ONLY" : "READ-WRITE")); + return cxt; +fail: + errsv = errno; + fdisk_free_context(cxt); + errno = errsv; + + DBG(CONTEXT, dbgprint("failed to initialize context for %s: %m", fname)); + return NULL; +} + +/** + * fdisk_free_context: + * @cxt: fdisk context + * + * Deallocates context struct. + */ +void fdisk_free_context(struct fdisk_context *cxt) +{ + if (!cxt) + return; + + DBG(CONTEXT, dbgprint("freeing context for %s", cxt->dev_path)); + close(cxt->dev_fd); + free(cxt->dev_path); + free(cxt->firstsector); + free(cxt); +} -- cgit v1.2.3