From: Viktor Mihajlovski <[email protected]> Remove the necessity for DASD-specific ioctls for partition handling. This allows to correctly handle DASD-backed virtio block devices.
Use bzero for initialization to make sure all struct members are properly cleared. Also changed partition list handling code to be more similar to upstream s390-tools fdasd. Further, enhanced error handling capabilities by providing a return code by fdasd_get_geometry. Code is largely backported from s390-tools project. Signed-off-by: Viktor Mihajlovski <[email protected]> Acked-by: Stefan Haberland <[email protected]> Signed-off-by: Hendrik Brueckner <[email protected]> --- include/parted/fdasd.in.h | 4 +- libparted/labels/fdasd.c | 165 +++++++++++++++++++++++++++++++-------------- 2 files changed, 118 insertions(+), 51 deletions(-) diff --git a/include/parted/fdasd.in.h b/include/parted/fdasd.in.h index 6f6a7e0..4e351c4 100644 --- a/include/parted/fdasd.in.h +++ b/include/parted/fdasd.in.h @@ -190,6 +190,8 @@ typedef struct format_data_t { #define BLKRRPART _IO(0x12,95) /* get block device sector size */ #define BLKSSZGET _IO(0x12,104) +/* device size in bytes (u64 *arg)*/ +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* get device geometry */ #define HDIO_GETGEO 0x0301 @@ -285,7 +287,7 @@ enum fdasd_failure { void fdasd_cleanup (fdasd_anchor_t *anchor); void fdasd_initialize_anchor (fdasd_anchor_t * anc); -void fdasd_get_geometry (const PedDevice *dev, fdasd_anchor_t *anc, int fd); +int fdasd_get_geometry (const PedDevice *dev, fdasd_anchor_t *anc, int fd); void fdasd_check_api_version (fdasd_anchor_t *anc, int fd); int fdasd_check_volume (fdasd_anchor_t *anc, int fd); int fdasd_write_labels (fdasd_anchor_t *anc, int fd); diff --git a/libparted/labels/fdasd.c b/libparted/labels/fdasd.c index 1f87937..f0f65cf 100644 --- a/libparted/labels/fdasd.c +++ b/libparted/labels/fdasd.c @@ -210,27 +210,7 @@ fdasd_initialize_anchor (fdasd_anchor_t * anc) partition_info_t *p = NULL; partition_info_t *q = NULL; - anc->devno = 0; - anc->dev_type = 0; - anc->used_partitions = 0; - - anc->silent = 0; - anc->verbose = 0; - anc->big_disk = 0; - anc->volid_specified = 0; - anc->config_specified = 0; - anc->auto_partition = 0; - anc->devname_specified = 0; - anc->print_table = 0; - - anc->option_reuse = 0; - anc->option_recreate = 0; - - anc->vlabel_changed = 0; - anc->vtoc_changed = 0; - anc->blksize = 0; - anc->fspace_trk = 0; - anc->label_pos = 0; + bzero(anc, sizeof(fdasd_anchor_t)); for (i=0; i<USABLE_PARTITIONS; i++) setpos(anc, i, -1); @@ -272,24 +252,18 @@ fdasd_initialize_anchor (fdasd_anchor_t * anc) if (p == NULL) fdasd_error(anc, malloc_failed, _("No room for partition info.")); - p->used = 0x00; - p->len_trk = 0; - p->start_trk = 0; - p->fspace_trk = 0; - p->type = 0; + bzero(p, sizeof(partition_info_t)); /* add p to double pointered list */ if (i == 1) { - anc->first = p; - p->prev = NULL; + anc->first = p; } else if (i == USABLE_PARTITIONS) { - anc->last = p; - p->next = NULL; + anc->last = p; p->prev = q; q->next = p; } else { - p->prev = q; - q->next = p; + p->prev = q; + q->next = p; } p->f1 = malloc(sizeof(format1_label_t)); @@ -947,15 +921,77 @@ fdasd_check_api_version (fdasd_anchor_t *anc, int f) } /* + * The following two functions match those in the DASD ECKD device driver. + * They are used to compute how many records of a given size can be stored + * in one track. + */ +static unsigned int ceil_quot(unsigned int d1, unsigned int d2) +{ + return (d1 + (d2 - 1)) / d2; +} + +/* kl: key length, dl: data length */ +static unsigned int recs_per_track(unsigned short dev_type, unsigned int kl, + unsigned int dl) +{ + int dn, kn; + + switch (dev_type) { + case DASD_3380_TYPE: + if (kl) + return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + + ceil_quot(dl + 12, 32)); + else + return 1499 / (15 + ceil_quot(dl + 12, 32)); + case DASD_3390_TYPE: + dn = ceil_quot(dl + 6, 232) + 1; + if (kl) { + kn = ceil_quot(kl + 6, 232) + 1; + return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + + 9 + ceil_quot(dl + 6 * dn, 34)); + } else + return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); + case DASD_9345_TYPE: + dn = ceil_quot(dl + 6, 232) + 1; + if (kl) { + kn = ceil_quot(kl + 6, 232) + 1; + return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + + ceil_quot(dl + 6 * dn, 34)); + } else + return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); + } + return 0; +} + +/* + * Verify that number of tracks (heads) per cylinder and number of + * sectors per track match the expected values for a given device type + * and block size. + * Returns 1 for a valid match and 0 otherwise. + */ +static int fdasd_verify_geometry(unsigned short dev_type, int blksize, + struct fdasd_hd_geometry *geometry) +{ + unsigned int expected_sectors; + if (geometry->heads != 15) + return 0; + expected_sectors = recs_per_track(dev_type, 0, blksize); + if (geometry->sectors == expected_sectors) + return 1; + return 0; +} + +/* * reads dasd geometry data */ -void +int fdasd_get_geometry (const PedDevice *dev, fdasd_anchor_t *anc, int f) { PDEBUG int blksize = 0; dasd_information_t dasd_info; struct dasd_eckd_characteristics *characteristics; + unsigned long long size_in_bytes; /* We can't get geometry from a regular file, so simulate something usable, for the sake of testing. */ @@ -979,6 +1015,11 @@ fdasd_get_geometry (const PedDevice *dev, fdasd_anchor_t *anc, int f) anc->geo.heads; anc->is_file = 1; } else { + if (ioctl(f, BLKGETSIZE64, &size_in_bytes) != 0) { + fdasd_error(anc, unable_to_ioctl, + "Could not retrieve disk size."); + } + if (ioctl(f, HDIO_GETGEO, &anc->geo) != 0) fdasd_error(anc, unable_to_ioctl, _("Could not retrieve disk geometry information.")); @@ -988,27 +1029,51 @@ fdasd_get_geometry (const PedDevice *dev, fdasd_anchor_t *anc, int f) _("Could not retrieve blocksize information.")); /* get disk type */ - if (ioctl(f, BIODASDINFO, &dasd_info) != 0) - fdasd_error(anc, unable_to_ioctl, - _("Could not retrieve disk information.")); - - characteristics = (struct dasd_eckd_characteristics *) - &dasd_info.characteristics; - if (characteristics->no_cyl == LV_COMPAT_CYL && - characteristics->long_no_cyl) - anc->hw_cylinders = characteristics->long_no_cyl; - else - anc->hw_cylinders = characteristics->no_cyl; + if (ioctl(f, BIODASDINFO, &dasd_info) != 0) { + /* verify that the geometry matches a 3390 DASD */ + if (!fdasd_verify_geometry(DASD_3390_TYPE, blksize, + &anc->geo)) { + fdasd_error(anc, wrong_disk_type, + "Disk geometry does not match a " + "DASD device of type 3390."); + goto error; + } + anc->dev_type = DASD_3390_TYPE; + anc->hw_cylinders = + size_in_bytes / (blksize * anc->geo.heads * anc->geo.sectors); + /* The VOL1 label on a CDL formatted ECKD DASD is in block 2 + * It will be verified later, if this position actually holds a + * valid label record. + */ + anc->label_pos = 2 * blksize; + /* A devno 0 is actually a valid devno, which could exist + * in the system. Since we use this number only to create + * a default volume serial, there is no serious conflict. + */ + anc->devno = 0; + } else { + characteristics = (struct dasd_eckd_characteristics *) + &dasd_info.characteristics; + if (characteristics->no_cyl == LV_COMPAT_CYL && + characteristics->long_no_cyl) + anc->hw_cylinders = characteristics->long_no_cyl; + else + anc->hw_cylinders = characteristics->no_cyl; + anc->dev_type = dasd_info.dev_type; + anc->label_pos = dasd_info.label_block * blksize; + anc->devno = dasd_info.devno; + anc->label_block = dasd_info.label_block; + anc->FBA_layout = dasd_info.FBA_layout; + } anc->is_file = 0; + anc->blksize = blksize; } - anc->dev_type = dasd_info.dev_type; - anc->blksize = blksize; - anc->label_pos = dasd_info.label_block * blksize; - anc->devno = dasd_info.devno; - anc->label_block = dasd_info.label_block; - anc->FBA_layout = dasd_info.FBA_layout; + return 1; + + error: + return 0; } /* -- 1.7.1

